1use crate::ipopt_data::IpoptDataHandle;
23use crate::ipopt_nlp::IpoptNlp;
24use crate::iterates_vector::IteratesVector;
25use pounce_common::cached::Cache;
26use pounce_common::types::Number;
27use pounce_linalg::{Matrix, SymMatrix, Vector};
28use std::cell::RefCell;
29use std::rc::Rc;
30
31pub struct IpoptCalculatedQuantities {
34 data: IpoptDataHandle,
35 nlp: Rc<RefCell<dyn IpoptNlp>>,
36
37 pub s_max: Number,
39 pub kappa_d: Number,
42
43 curr_slack_x_l_cache: RefCell<Cache<Rc<dyn Vector>>>,
52 curr_slack_x_u_cache: RefCell<Cache<Rc<dyn Vector>>>,
53 curr_slack_s_l_cache: RefCell<Cache<Rc<dyn Vector>>>,
54 curr_slack_s_u_cache: RefCell<Cache<Rc<dyn Vector>>>,
55 curr_sigma_x_cache: RefCell<Cache<Rc<dyn Vector>>>,
56 curr_sigma_s_cache: RefCell<Cache<Rc<dyn Vector>>>,
57}
58
59fn rc_from(v: Box<dyn Vector>) -> Rc<dyn Vector> {
62 Rc::from(v)
63}
64
65impl IpoptCalculatedQuantities {
66 pub fn new(data: IpoptDataHandle, nlp: Rc<RefCell<dyn IpoptNlp>>) -> Self {
67 Self {
68 data,
69 nlp,
70 s_max: 100.0,
71 kappa_d: 1e-5,
72 curr_slack_x_l_cache: RefCell::new(Cache::new(1)),
73 curr_slack_x_u_cache: RefCell::new(Cache::new(1)),
74 curr_slack_s_l_cache: RefCell::new(Cache::new(1)),
75 curr_slack_s_u_cache: RefCell::new(Cache::new(1)),
76 curr_sigma_x_cache: RefCell::new(Cache::new(1)),
77 curr_sigma_s_cache: RefCell::new(Cache::new(1)),
78 }
79 }
80
81 pub fn data(&self) -> &IpoptDataHandle {
82 &self.data
83 }
84
85 pub fn nlp(&self) -> &Rc<RefCell<dyn IpoptNlp>> {
86 &self.nlp
87 }
88
89 pub(crate) fn curr_iv(&self) -> IteratesVector {
90 let Some(iv) = self.data.borrow().curr.as_ref().cloned() else {
91 unreachable!("IpoptCalculatedQuantities: curr iterate not set");
92 };
93 iv
94 }
95
96 fn trial_iv(&self) -> IteratesVector {
97 let Some(iv) = self.data.borrow().trial.as_ref().cloned() else {
98 unreachable!("IpoptCalculatedQuantities: trial iterate not set");
99 };
100 iv
101 }
102
103 fn calc_slack_l(p: &dyn Matrix, x: &dyn Vector, x_bound: &dyn Vector) -> Rc<dyn Vector> {
110 let mut result = x_bound.make_new();
111 result.copy(x_bound);
112 p.trans_mult_vector(1.0, x, -1.0, &mut *result);
114 rc_from(result)
115 }
116
117 fn calc_slack_u(p: &dyn Matrix, x: &dyn Vector, x_bound: &dyn Vector) -> Rc<dyn Vector> {
118 let mut result = x_bound.make_new();
119 result.copy(x_bound);
120 p.trans_mult_vector(-1.0, x, 1.0, &mut *result);
122 rc_from(result)
123 }
124
125 pub fn curr_slack_x_l(&self) -> Rc<dyn Vector> {
126 let iv = self.curr_iv();
127 {
128 let cache = self.curr_slack_x_l_cache.borrow();
129 if let Some(v) = cache.get(&[iv.x.as_tagged()], &[]) {
130 return v;
131 }
132 }
133 let nlp = self.nlp.borrow();
134 let v = Self::calc_slack_l(&*nlp.px_l(), &*iv.x, nlp.x_l());
135 self.curr_slack_x_l_cache
136 .borrow_mut()
137 .add(v.clone(), &[iv.x.as_tagged()], &[]);
138 v
139 }
140
141 pub fn curr_slack_x_u(&self) -> Rc<dyn Vector> {
142 let iv = self.curr_iv();
143 {
144 let cache = self.curr_slack_x_u_cache.borrow();
145 if let Some(v) = cache.get(&[iv.x.as_tagged()], &[]) {
146 return v;
147 }
148 }
149 let nlp = self.nlp.borrow();
150 let v = Self::calc_slack_u(&*nlp.px_u(), &*iv.x, nlp.x_u());
151 self.curr_slack_x_u_cache
152 .borrow_mut()
153 .add(v.clone(), &[iv.x.as_tagged()], &[]);
154 v
155 }
156
157 pub fn curr_slack_s_l(&self) -> Rc<dyn Vector> {
158 let iv = self.curr_iv();
159 {
160 let cache = self.curr_slack_s_l_cache.borrow();
161 if let Some(v) = cache.get(&[iv.s.as_tagged()], &[]) {
162 return v;
163 }
164 }
165 let nlp = self.nlp.borrow();
166 let v = Self::calc_slack_l(&*nlp.pd_l(), &*iv.s, nlp.d_l());
167 self.curr_slack_s_l_cache
168 .borrow_mut()
169 .add(v.clone(), &[iv.s.as_tagged()], &[]);
170 v
171 }
172
173 pub fn curr_slack_s_u(&self) -> Rc<dyn Vector> {
174 let iv = self.curr_iv();
175 {
176 let cache = self.curr_slack_s_u_cache.borrow();
177 if let Some(v) = cache.get(&[iv.s.as_tagged()], &[]) {
178 return v;
179 }
180 }
181 let nlp = self.nlp.borrow();
182 let v = Self::calc_slack_u(&*nlp.pd_u(), &*iv.s, nlp.d_u());
183 self.curr_slack_s_u_cache
184 .borrow_mut()
185 .add(v.clone(), &[iv.s.as_tagged()], &[]);
186 v
187 }
188
189 pub fn trial_slack_x_l(&self) -> Rc<dyn Vector> {
190 let iv = self.trial_iv();
191 let nlp = self.nlp.borrow();
192 Self::calc_slack_l(&*nlp.px_l(), &*iv.x, nlp.x_l())
193 }
194
195 pub fn trial_slack_x_u(&self) -> Rc<dyn Vector> {
196 let iv = self.trial_iv();
197 let nlp = self.nlp.borrow();
198 Self::calc_slack_u(&*nlp.px_u(), &*iv.x, nlp.x_u())
199 }
200
201 pub fn trial_slack_s_l(&self) -> Rc<dyn Vector> {
202 let iv = self.trial_iv();
203 let nlp = self.nlp.borrow();
204 Self::calc_slack_l(&*nlp.pd_l(), &*iv.s, nlp.d_l())
205 }
206
207 pub fn trial_slack_s_u(&self) -> Rc<dyn Vector> {
208 let iv = self.trial_iv();
209 let nlp = self.nlp.borrow();
210 Self::calc_slack_u(&*nlp.pd_u(), &*iv.s, nlp.d_u())
211 }
212
213 pub fn curr_grad_f(&self) -> Rc<dyn Vector> {
218 let iv = self.curr_iv();
219 let mut nlp = self.nlp.borrow_mut();
220 let mut g = iv.x.make_new();
221 nlp.eval_grad_f(&*iv.x, &mut *g);
222 rc_from(g)
223 }
224
225 pub fn trial_grad_f(&self) -> Rc<dyn Vector> {
226 let iv = self.trial_iv();
227 let mut nlp = self.nlp.borrow_mut();
228 let mut g = iv.x.make_new();
229 nlp.eval_grad_f(&*iv.x, &mut *g);
230 rc_from(g)
231 }
232
233 pub fn curr_c(&self) -> Rc<dyn Vector> {
234 let iv = self.curr_iv();
235 let m = self.nlp.borrow().m_eq();
236 let mut nlp = self.nlp.borrow_mut();
237 let mut c = iv.y_c.make_new();
238 debug_assert_eq!(c.dim(), m);
239 nlp.eval_c(&*iv.x, &mut *c);
240 rc_from(c)
241 }
242
243 pub fn trial_c(&self) -> Rc<dyn Vector> {
244 let iv = self.trial_iv();
245 let mut nlp = self.nlp.borrow_mut();
246 let mut c = iv.y_c.make_new();
247 nlp.eval_c(&*iv.x, &mut *c);
248 rc_from(c)
249 }
250
251 pub fn curr_d(&self) -> Rc<dyn Vector> {
252 let iv = self.curr_iv();
253 let mut nlp = self.nlp.borrow_mut();
254 let mut d = iv.s.make_new();
255 nlp.eval_d(&*iv.x, &mut *d);
256 rc_from(d)
257 }
258
259 pub fn trial_d(&self) -> Rc<dyn Vector> {
260 let iv = self.trial_iv();
261 let mut nlp = self.nlp.borrow_mut();
262 let mut d = iv.s.make_new();
263 nlp.eval_d(&*iv.x, &mut *d);
264 rc_from(d)
265 }
266
267 pub fn curr_jac_c(&self) -> Rc<dyn Matrix> {
268 let iv = self.curr_iv();
269 self.nlp.borrow_mut().eval_jac_c(&*iv.x)
270 }
271
272 pub fn curr_jac_d(&self) -> Rc<dyn Matrix> {
273 let iv = self.curr_iv();
274 self.nlp.borrow_mut().eval_jac_d(&*iv.x)
275 }
276
277 pub fn curr_exact_hessian(&self) -> Rc<dyn SymMatrix> {
278 let iv = self.curr_iv();
279 self.nlp
280 .borrow_mut()
281 .eval_h(&*iv.x, 1.0, &*iv.y_c, &*iv.y_d)
282 }
283
284 pub fn curr_d_minus_s(&self) -> Rc<dyn Vector> {
286 let iv = self.curr_iv();
287 let d = self.curr_d();
288 let mut tmp = iv.s.make_new();
289 tmp.add_two_vectors(1.0, &*d, -1.0, &*iv.s, 0.0);
291 rc_from(tmp)
292 }
293
294 pub fn trial_d_minus_s(&self) -> Rc<dyn Vector> {
295 let iv = self.trial_iv();
296 let d = self.trial_d();
297 let mut tmp = iv.s.make_new();
298 tmp.add_two_vectors(1.0, &*d, -1.0, &*iv.s, 0.0);
299 rc_from(tmp)
300 }
301
302 pub fn curr_jac_c_t_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector> {
305 let iv = self.curr_iv();
306 let jac_c = self.curr_jac_c();
307 let mut tmp = iv.x.make_new();
308 jac_c.trans_mult_vector(1.0, vec, 0.0, &mut *tmp);
309 rc_from(tmp)
310 }
311
312 pub fn curr_jac_d_t_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector> {
314 let iv = self.curr_iv();
315 let jac_d = self.curr_jac_d();
316 let mut tmp = iv.x.make_new();
317 jac_d.trans_mult_vector(1.0, vec, 0.0, &mut *tmp);
318 rc_from(tmp)
319 }
320
321 pub fn curr_jac_c_t_times_curr_y_c(&self) -> Rc<dyn Vector> {
322 let iv = self.curr_iv();
323 self.curr_jac_c_t_times_vec(&*iv.y_c)
324 }
325
326 pub fn curr_jac_d_t_times_curr_y_d(&self) -> Rc<dyn Vector> {
327 let iv = self.curr_iv();
328 self.curr_jac_d_t_times_vec(&*iv.y_d)
329 }
330
331 pub fn curr_jac_c_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector> {
333 let iv = self.curr_iv();
334 let jac_c = self.curr_jac_c();
335 let mut tmp = iv.y_c.make_new();
336 jac_c.mult_vector(1.0, vec, 0.0, &mut *tmp);
337 rc_from(tmp)
338 }
339
340 pub fn curr_jac_d_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector> {
342 let iv = self.curr_iv();
343 let jac_d = self.curr_jac_d();
344 let mut tmp = iv.s.make_new();
345 jac_d.mult_vector(1.0, vec, 0.0, &mut *tmp);
346 rc_from(tmp)
347 }
348
349 pub fn curr_grad_lag_x(&self) -> Rc<dyn Vector> {
356 let iv = self.curr_iv();
357 let grad_f = self.curr_grad_f();
358 let jc_t_y_c = self.curr_jac_c_t_times_curr_y_c();
359 let jd_t_y_d = self.curr_jac_d_t_times_curr_y_d();
360
361 let mut tmp = iv.x.make_new();
362 tmp.copy(&*grad_f);
363 tmp.add_two_vectors(1.0, &*jc_t_y_c, 1.0, &*jd_t_y_d, 1.0);
364
365 let nlp = self.nlp.borrow();
366 nlp.px_l().mult_vector(-1.0, &*iv.z_l, 1.0, &mut *tmp);
367 nlp.px_u().mult_vector(1.0, &*iv.z_u, 1.0, &mut *tmp);
368 rc_from(tmp)
369 }
370
371 pub fn curr_grad_lag_s(&self) -> Rc<dyn Vector> {
374 let iv = self.curr_iv();
375 let mut tmp = iv.y_d.make_new();
376 let nlp = self.nlp.borrow();
377 nlp.pd_u().mult_vector(1.0, &*iv.v_u, 0.0, &mut *tmp);
379 nlp.pd_l().mult_vector(-1.0, &*iv.v_l, 1.0, &mut *tmp);
381 tmp.axpy(-1.0, &*iv.y_d);
383 rc_from(tmp)
384 }
385
386 fn calc_compl(slack: &dyn Vector, mult: &dyn Vector) -> Rc<dyn Vector> {
391 let mut result = slack.make_new();
392 result.copy(slack);
393 result.element_wise_multiply(mult);
394 rc_from(result)
395 }
396
397 pub fn curr_compl_x_l(&self) -> Rc<dyn Vector> {
398 let slack = self.curr_slack_x_l();
399 let z_l = self.curr_iv().z_l;
400 Self::calc_compl(&*slack, &*z_l)
401 }
402
403 pub fn curr_compl_x_u(&self) -> Rc<dyn Vector> {
404 let slack = self.curr_slack_x_u();
405 let z_u = self.curr_iv().z_u;
406 Self::calc_compl(&*slack, &*z_u)
407 }
408
409 pub fn curr_compl_s_l(&self) -> Rc<dyn Vector> {
410 let slack = self.curr_slack_s_l();
411 let v_l = self.curr_iv().v_l;
412 Self::calc_compl(&*slack, &*v_l)
413 }
414
415 pub fn curr_compl_s_u(&self) -> Rc<dyn Vector> {
416 let slack = self.curr_slack_s_u();
417 let v_u = self.curr_iv().v_u;
418 Self::calc_compl(&*slack, &*v_u)
419 }
420
421 pub fn curr_relaxed_compl_x_l(&self) -> Rc<dyn Vector> {
424 let mu = self.data.borrow().curr_mu;
425 let mut r = self.curr_compl_x_l().make_new();
426 r.copy(&*self.curr_compl_x_l());
427 r.add_scalar(-mu);
428 rc_from(r)
429 }
430
431 pub fn curr_relaxed_compl_x_u(&self) -> Rc<dyn Vector> {
432 let mu = self.data.borrow().curr_mu;
433 let mut r = self.curr_compl_x_u().make_new();
434 r.copy(&*self.curr_compl_x_u());
435 r.add_scalar(-mu);
436 rc_from(r)
437 }
438
439 pub fn curr_relaxed_compl_s_l(&self) -> Rc<dyn Vector> {
440 let mu = self.data.borrow().curr_mu;
441 let mut r = self.curr_compl_s_l().make_new();
442 r.copy(&*self.curr_compl_s_l());
443 r.add_scalar(-mu);
444 rc_from(r)
445 }
446
447 pub fn curr_relaxed_compl_s_u(&self) -> Rc<dyn Vector> {
448 let mu = self.data.borrow().curr_mu;
449 let mut r = self.curr_compl_s_u().make_new();
450 r.copy(&*self.curr_compl_s_u());
451 r.add_scalar(-mu);
452 rc_from(r)
453 }
454
455 pub fn curr_sigma_x(&self) -> Rc<dyn Vector> {
464 let iv = self.curr_iv();
465 {
466 let cache = self.curr_sigma_x_cache.borrow();
467 if let Some(v) = cache.get(
468 &[iv.x.as_tagged(), iv.z_l.as_tagged(), iv.z_u.as_tagged()],
469 &[],
470 ) {
471 return v;
472 }
473 }
474 let slack_l = self.curr_slack_x_l();
475 let slack_u = self.curr_slack_x_u();
476
477 let mut sigma = iv.x.make_new();
478 sigma.set(0.0);
479
480 let nlp = self.nlp.borrow();
481 nlp.px_l()
482 .add_m_sinv_z(1.0, &*slack_l, &*iv.z_l, &mut *sigma);
483 nlp.px_u()
484 .add_m_sinv_z(1.0, &*slack_u, &*iv.z_u, &mut *sigma);
485 let v = rc_from(sigma);
486 self.curr_sigma_x_cache.borrow_mut().add(
487 v.clone(),
488 &[iv.x.as_tagged(), iv.z_l.as_tagged(), iv.z_u.as_tagged()],
489 &[],
490 );
491 v
492 }
493
494 pub fn curr_sigma_s(&self) -> Rc<dyn Vector> {
495 let iv = self.curr_iv();
496 {
497 let cache = self.curr_sigma_s_cache.borrow();
498 if let Some(v) = cache.get(
499 &[iv.s.as_tagged(), iv.v_l.as_tagged(), iv.v_u.as_tagged()],
500 &[],
501 ) {
502 return v;
503 }
504 }
505 let slack_l = self.curr_slack_s_l();
506 let slack_u = self.curr_slack_s_u();
507
508 let mut sigma = iv.s.make_new();
509 sigma.set(0.0);
510
511 let nlp = self.nlp.borrow();
512 nlp.pd_l()
513 .add_m_sinv_z(1.0, &*slack_l, &*iv.v_l, &mut *sigma);
514 nlp.pd_u()
515 .add_m_sinv_z(1.0, &*slack_u, &*iv.v_u, &mut *sigma);
516 let v = rc_from(sigma);
517 self.curr_sigma_s_cache.borrow_mut().add(
518 v.clone(),
519 &[iv.s.as_tagged(), iv.v_l.as_tagged(), iv.v_u.as_tagged()],
520 &[],
521 );
522 v
523 }
524
525 pub fn curr_f(&self) -> Number {
542 let iv = self.curr_iv();
543 let mut nlp = self.nlp.borrow_mut();
544 nlp.eval_f(&*iv.x)
545 }
546
547 pub fn unscaled_curr_f(&self) -> Number {
554 let scaled = self.curr_f();
555 let factor = self.nlp.borrow().obj_scaling_factor();
556 if factor == 0.0 {
557 scaled
558 } else {
559 scaled / factor
560 }
561 }
562
563 pub fn trial_f(&self) -> Number {
564 let iv = self.trial_iv();
565 let mut nlp = self.nlp.borrow_mut();
566 nlp.eval_f(&*iv.x)
567 }
568
569 fn barrier_obj_at(
570 &self,
571 f: Number,
572 s_x_l: &dyn Vector,
573 s_x_u: &dyn Vector,
574 s_s_l: &dyn Vector,
575 s_s_u: &dyn Vector,
576 ) -> Number {
577 let mu = self.data.borrow().curr_mu;
578 let log_sum = s_x_l.sum_logs() + s_x_u.sum_logs() + s_s_l.sum_logs() + s_s_u.sum_logs();
579 let mut phi = f - mu * log_sum;
580 if self.kappa_d > 0.0 {
581 let di = self.damping_indicators();
582 phi += self.kappa_d * mu * s_x_l.dot(&*di.x_l);
583 phi += self.kappa_d * mu * s_x_u.dot(&*di.x_u);
584 phi += self.kappa_d * mu * s_s_l.dot(&*di.s_l);
585 phi += self.kappa_d * mu * s_s_u.dot(&*di.s_u);
586 }
587 phi
588 }
589
590 pub fn curr_barrier_obj(&self) -> Number {
591 let f = self.curr_f();
592 let s_x_l = self.curr_slack_x_l();
593 let s_x_u = self.curr_slack_x_u();
594 let s_s_l = self.curr_slack_s_l();
595 let s_s_u = self.curr_slack_s_u();
596 self.barrier_obj_at(f, &*s_x_l, &*s_x_u, &*s_s_l, &*s_s_u)
597 }
598
599 pub fn trial_barrier_obj(&self) -> Number {
600 let f = self.trial_f();
601 let s_x_l = self.trial_slack_x_l();
602 let s_x_u = self.trial_slack_x_u();
603 let s_s_l = self.trial_slack_s_l();
604 let s_s_u = self.trial_slack_s_u();
605 self.barrier_obj_at(f, &*s_x_l, &*s_x_u, &*s_s_l, &*s_s_u)
606 }
607
608 pub fn curr_grad_barrier_obj_x(&self) -> Rc<dyn Vector> {
612 let iv = self.curr_iv();
613 let mu = self.data.borrow().curr_mu;
614 let s_l = self.curr_slack_x_l();
615 let s_u = self.curr_slack_x_u();
616
617 let mut inv_s_l = s_l.make_new();
618 inv_s_l.copy(&*s_l);
619 inv_s_l.element_wise_reciprocal();
620 let mut inv_s_u = s_u.make_new();
621 inv_s_u.copy(&*s_u);
622 inv_s_u.element_wise_reciprocal();
623
624 let grad_f = self.curr_grad_f();
625 let mut tmp = iv.x.make_new();
626 tmp.copy(&*grad_f);
627 let nlp = self.nlp.borrow();
628 nlp.px_l().mult_vector(-mu, &*inv_s_l, 1.0, &mut *tmp);
630 nlp.px_u().mult_vector(mu, &*inv_s_u, 1.0, &mut *tmp);
632
633 if self.kappa_d > 0.0 {
634 let di = self.damping_indicators();
635 nlp.px_l()
637 .mult_vector(self.kappa_d * mu, &*di.x_l, 1.0, &mut *tmp);
638 nlp.px_u()
640 .mult_vector(-self.kappa_d * mu, &*di.x_u, 1.0, &mut *tmp);
641 }
642 rc_from(tmp)
643 }
644
645 pub fn curr_grad_barrier_obj_s(&self) -> Rc<dyn Vector> {
648 let iv = self.curr_iv();
649 let mu = self.data.borrow().curr_mu;
650 let s_l = self.curr_slack_s_l();
651 let s_u = self.curr_slack_s_u();
652
653 let mut inv_s_l = s_l.make_new();
654 inv_s_l.copy(&*s_l);
655 inv_s_l.element_wise_reciprocal();
656 let mut inv_s_u = s_u.make_new();
657 inv_s_u.copy(&*s_u);
658 inv_s_u.element_wise_reciprocal();
659
660 let mut tmp = iv.s.make_new();
661 tmp.set(0.0);
662 let nlp = self.nlp.borrow();
663 nlp.pd_l().mult_vector(-mu, &*inv_s_l, 1.0, &mut *tmp);
664 nlp.pd_u().mult_vector(mu, &*inv_s_u, 1.0, &mut *tmp);
665
666 if self.kappa_d > 0.0 {
667 let di = self.damping_indicators();
668 nlp.pd_l()
669 .mult_vector(self.kappa_d * mu, &*di.s_l, 1.0, &mut *tmp);
670 nlp.pd_u()
671 .mult_vector(-self.kappa_d * mu, &*di.s_u, 1.0, &mut *tmp);
672 }
673 rc_from(tmp)
674 }
675
676 pub fn curr_grad_barr_t_delta(&self, delta_x: &dyn Vector, delta_s: &dyn Vector) -> Number {
688 let g_x = self.curr_grad_barrier_obj_x();
689 let g_s = self.curr_grad_barrier_obj_s();
690 g_x.dot(delta_x) + g_s.dot(delta_s)
691 }
692
693 pub fn curr_dwd(&self, delta_x: &dyn Vector, delta_s: &dyn Vector) -> Number {
700 let mut dwd: Number = 0.0;
701
702 if let Some(w) = self.data.borrow().w.clone() {
704 let mut wd = delta_x.make_new();
705 w.mult_vector(1.0, delta_x, 0.0, &mut *wd);
706 dwd += wd.dot(delta_x);
707 }
708
709 let sigma_x = self.curr_sigma_x();
711 let mut tmp_x = delta_x.make_new();
712 tmp_x.copy(delta_x);
713 tmp_x.element_wise_multiply(&*sigma_x);
714 dwd += tmp_x.dot(delta_x);
715
716 let sigma_s = self.curr_sigma_s();
718 let mut tmp_s = delta_s.make_new();
719 tmp_s.copy(delta_s);
720 tmp_s.element_wise_multiply(&*sigma_s);
721 dwd += tmp_s.dot(delta_s);
722
723 let pert = self.data.borrow().perturbations;
725 if pert.delta_x != 0.0 {
726 let nx = delta_x.nrm2();
727 dwd += pert.delta_x * nx * nx;
728 }
729 if pert.delta_s != 0.0 {
730 let ns = delta_s.nrm2();
731 dwd += pert.delta_s * ns * ns;
732 }
733
734 dwd.max(0.0)
735 }
736
737 pub fn curr_constraint_violation(&self) -> Number {
745 let c = self.curr_c();
746 let dms = self.curr_d_minus_s();
747 c.asum() + dms.asum()
748 }
749
750 pub fn trial_constraint_violation(&self) -> Number {
751 let c = self.trial_c();
752 let dms = self.trial_d_minus_s();
753 c.asum() + dms.asum()
754 }
755
756 pub fn curr_primal_infeasibility_max(&self) -> Number {
761 let c = self.curr_c();
762 let dms = self.curr_d_minus_s();
763 c.amax().max(dms.amax())
764 }
765
766 pub fn curr_dual_infeasibility_max(&self) -> Number {
769 let glx = self.curr_grad_lag_x();
770 let gls = self.curr_grad_lag_s();
771 glx.amax().max(gls.amax())
772 }
773
774 pub fn curr_infeasibility_stationarity(&self) -> Number {
784 let c = self.curr_c();
785 let dms = self.curr_d_minus_s();
786 let jc_t_c = self.curr_jac_c_t_times_vec(&*c);
787 let jd_t_dms = self.curr_jac_d_t_times_vec(&*dms);
788 let mut grad = jc_t_c.make_new();
789 grad.add_two_vectors(1.0, &*jc_t_c, 1.0, &*jd_t_dms, 0.0);
790 let viol = c.amax().max(dms.amax());
791 grad.amax() / viol.max(1.0)
792 }
793
794 pub fn curr_avrg_compl(&self) -> Number {
802 let iv = self.curr_iv();
803 let n = iv.z_l.dim() + iv.z_u.dim() + iv.v_l.dim() + iv.v_u.dim();
804 if n == 0 {
805 return 0.0;
806 }
807 let s_x_l = self.curr_slack_x_l();
808 let s_x_u = self.curr_slack_x_u();
809 let s_s_l = self.curr_slack_s_l();
810 let s_s_u = self.curr_slack_s_u();
811 let mut acc = iv.z_l.dot(&*s_x_l);
812 acc += iv.z_u.dot(&*s_x_u);
813 acc += iv.v_l.dot(&*s_s_l);
814 acc += iv.v_u.dot(&*s_s_u);
815 acc / Number::from(n)
816 }
817
818 pub fn curr_complementarity_min(&self) -> Number {
825 let cxl = self.curr_compl_x_l();
826 let cxu = self.curr_compl_x_u();
827 let csl = self.curr_compl_s_l();
828 let csu = self.curr_compl_s_u();
829 let m = |v: &Rc<dyn Vector>| {
830 if v.dim() == 0 {
831 Number::INFINITY
832 } else {
833 v.min()
834 }
835 };
836 let acc = m(&cxl).min(m(&cxu)).min(m(&csl)).min(m(&csu));
837 if acc.is_infinite() {
838 0.0
839 } else {
840 acc
841 }
842 }
843
844 pub fn curr_complementarity_max(&self) -> Number {
852 self.curr_compl_x_l()
853 .amax()
854 .max(self.curr_compl_x_u().amax())
855 .max(self.curr_compl_s_l().amax())
856 .max(self.curr_compl_s_u().amax())
857 }
858
859 pub fn curr_centrality_measure(&self) -> Number {
865 let avrg = self.curr_avrg_compl();
866 if avrg <= 0.0 {
867 return 1.0;
868 }
869 self.curr_complementarity_min() / avrg
870 }
871
872 pub fn curr_barrier_error(&self) -> Number {
880 let iv = self.curr_iv();
881 let (s_d, s_c) = self.optimality_error_scaling(&iv);
882
883 let glx = self.curr_grad_lag_x();
884 let gls = self.curr_grad_lag_s();
885 let dual = glx.amax().max(gls.amax()) / s_d;
886
887 let c = self.curr_c();
888 let dms = self.curr_d_minus_s();
889 let primal = c.amax().max(dms.amax());
890
891 let compl = self
892 .curr_relaxed_compl_x_l()
893 .amax()
894 .max(self.curr_relaxed_compl_x_u().amax())
895 .max(self.curr_relaxed_compl_s_l().amax())
896 .max(self.curr_relaxed_compl_s_u().amax())
897 / s_c;
898
899 dual.max(primal).max(compl)
900 }
901
902 pub fn curr_nlp_error(&self) -> Number {
916 let iv = self.curr_iv();
917 let (s_d, s_c) = self.optimality_error_scaling(&iv);
918
919 let glx = self.curr_grad_lag_x();
921 let gls = self.curr_grad_lag_s();
922 let dual = glx.amax().max(gls.amax()) / s_d;
923
924 let c = self.curr_c();
926 let dms = self.curr_d_minus_s();
927 let primal = c.amax().max(dms.amax());
928
929 let compl = self
931 .curr_compl_x_l()
932 .amax()
933 .max(self.curr_compl_x_u().amax())
934 .max(self.curr_compl_s_l().amax())
935 .max(self.curr_compl_s_u().amax())
936 / s_c;
937
938 dual.max(primal).max(compl)
939 }
940
941 fn optimality_error_scaling(&self, iv: &IteratesVector) -> (Number, Number) {
944 let s_max = self.s_max;
945
946 let n_c = iv.z_l.dim() + iv.z_u.dim() + iv.v_l.dim() + iv.v_u.dim();
949 let s_c = if n_c == 0 {
950 1.0
951 } else {
952 let asum = iv.z_l.asum() + iv.z_u.asum() + iv.v_l.asum() + iv.v_u.asum();
953 (s_max.max(asum / Number::from(n_c))) / s_max
954 };
955
956 let n_d =
958 iv.y_c.dim() + iv.y_d.dim() + iv.z_l.dim() + iv.z_u.dim() + iv.v_l.dim() + iv.v_u.dim();
959 let s_d = if n_d == 0 {
960 1.0
961 } else {
962 let asum = iv.y_c.asum()
963 + iv.y_d.asum()
964 + iv.z_l.asum()
965 + iv.z_u.asum()
966 + iv.v_l.asum()
967 + iv.v_u.asum();
968 (s_max.max(asum / Number::from(n_d))) / s_max
969 };
970
971 (s_d, s_c)
972 }
973
974 pub fn trial_jac_c(&self) -> Rc<dyn Matrix> {
982 let iv = self.trial_iv();
983 self.nlp.borrow_mut().eval_jac_c(&*iv.x)
984 }
985
986 pub fn trial_jac_d(&self) -> Rc<dyn Matrix> {
987 let iv = self.trial_iv();
988 self.nlp.borrow_mut().eval_jac_d(&*iv.x)
989 }
990
991 pub fn trial_grad_lag_x(&self) -> Rc<dyn Vector> {
993 let iv = self.trial_iv();
994 let grad_f = self.trial_grad_f();
995 let jac_c = self.trial_jac_c();
996 let jac_d = self.trial_jac_d();
997
998 let mut jc_t = iv.x.make_new();
999 jac_c.trans_mult_vector(1.0, &*iv.y_c, 0.0, &mut *jc_t);
1000 let mut jd_t = iv.x.make_new();
1001 jac_d.trans_mult_vector(1.0, &*iv.y_d, 0.0, &mut *jd_t);
1002
1003 let mut tmp = iv.x.make_new();
1004 tmp.copy(&*grad_f);
1005 tmp.add_two_vectors(1.0, &*jc_t, 1.0, &*jd_t, 1.0);
1006
1007 let nlp = self.nlp.borrow();
1008 nlp.px_l().mult_vector(-1.0, &*iv.z_l, 1.0, &mut *tmp);
1009 nlp.px_u().mult_vector(1.0, &*iv.z_u, 1.0, &mut *tmp);
1010 rc_from(tmp)
1011 }
1012
1013 pub fn trial_grad_lag_s(&self) -> Rc<dyn Vector> {
1015 let iv = self.trial_iv();
1016 let mut tmp = iv.y_d.make_new();
1017 let nlp = self.nlp.borrow();
1018 nlp.pd_u().mult_vector(1.0, &*iv.v_u, 0.0, &mut *tmp);
1019 nlp.pd_l().mult_vector(-1.0, &*iv.v_l, 1.0, &mut *tmp);
1020 tmp.axpy(-1.0, &*iv.y_d);
1021 rc_from(tmp)
1022 }
1023
1024 pub fn trial_compl_x_l(&self) -> Rc<dyn Vector> {
1025 Self::calc_compl(&*self.trial_slack_x_l(), &*self.trial_iv().z_l)
1026 }
1027
1028 pub fn trial_compl_x_u(&self) -> Rc<dyn Vector> {
1029 Self::calc_compl(&*self.trial_slack_x_u(), &*self.trial_iv().z_u)
1030 }
1031
1032 pub fn trial_compl_s_l(&self) -> Rc<dyn Vector> {
1033 Self::calc_compl(&*self.trial_slack_s_l(), &*self.trial_iv().v_l)
1034 }
1035
1036 pub fn trial_compl_s_u(&self) -> Rc<dyn Vector> {
1037 Self::calc_compl(&*self.trial_slack_s_u(), &*self.trial_iv().v_u)
1038 }
1039
1040 fn relaxed_compl_asum(blocks: &[Rc<dyn Vector>], mu: Number) -> Number {
1042 let mut acc = 0.0;
1043 for compl in blocks {
1044 if compl.dim() == 0 {
1045 continue;
1046 }
1047 let mut r = compl.make_new();
1048 r.copy(&**compl);
1049 r.add_scalar(-mu);
1050 acc += r.asum();
1051 }
1052 acc
1053 }
1054
1055 pub fn curr_primal_dual_system_error(&self, mu: Number) -> Number {
1063 let iv = self.curr_iv();
1064 let n_dual = iv.x.dim() + iv.s.dim();
1065 let dual_inf =
1066 (self.curr_grad_lag_x().asum() + self.curr_grad_lag_s().asum()) / Number::from(n_dual);
1067
1068 let n_primal = iv.y_c.dim() + iv.y_d.dim();
1069 let primal_inf = if n_primal > 0 {
1070 (self.curr_c().asum() + self.curr_d_minus_s().asum()) / Number::from(n_primal)
1071 } else {
1072 0.0
1073 };
1074
1075 let n_cmpl = iv.z_l.dim() + iv.z_u.dim() + iv.v_l.dim() + iv.v_u.dim();
1076 let cmpl = if n_cmpl > 0 {
1077 Self::relaxed_compl_asum(
1078 &[
1079 self.curr_compl_x_l(),
1080 self.curr_compl_x_u(),
1081 self.curr_compl_s_l(),
1082 self.curr_compl_s_u(),
1083 ],
1084 mu,
1085 ) / Number::from(n_cmpl)
1086 } else {
1087 0.0
1088 };
1089
1090 dual_inf + primal_inf + cmpl
1091 }
1092
1093 pub fn trial_primal_dual_system_error(&self, mu: Number) -> Number {
1096 let iv = self.trial_iv();
1097 let n_dual = iv.x.dim() + iv.s.dim();
1098 let dual_inf = (self.trial_grad_lag_x().asum() + self.trial_grad_lag_s().asum())
1099 / Number::from(n_dual);
1100
1101 let n_primal = iv.y_c.dim() + iv.y_d.dim();
1102 let primal_inf = if n_primal > 0 {
1103 (self.trial_c().asum() + self.trial_d_minus_s().asum()) / Number::from(n_primal)
1104 } else {
1105 0.0
1106 };
1107
1108 let n_cmpl = iv.z_l.dim() + iv.z_u.dim() + iv.v_l.dim() + iv.v_u.dim();
1109 let cmpl = if n_cmpl > 0 {
1110 Self::relaxed_compl_asum(
1111 &[
1112 self.trial_compl_x_l(),
1113 self.trial_compl_x_u(),
1114 self.trial_compl_s_l(),
1115 self.trial_compl_s_u(),
1116 ],
1117 mu,
1118 ) / Number::from(n_cmpl)
1119 } else {
1120 0.0
1121 };
1122
1123 dual_inf + primal_inf + cmpl
1124 }
1125
1126 fn damping_indicators(&self) -> DampingIndicators {
1137 let nlp = self.nlp.borrow();
1138
1139 let mut tmp_x_l = nlp.x_l().make_new();
1140 tmp_x_l.set(1.0);
1141 let mut tmp_x_u = nlp.x_u().make_new();
1142 tmp_x_u.set(1.0);
1143 let mut tmp_x = self.curr_iv().x.make_new();
1144 nlp.px_l().mult_vector(1.0, &*tmp_x_l, 0.0, &mut *tmp_x);
1145 nlp.px_u().mult_vector(-1.0, &*tmp_x_u, 1.0, &mut *tmp_x);
1146 let mut d_x_l = nlp.x_l().make_new();
1147 nlp.px_l().trans_mult_vector(1.0, &*tmp_x, 0.0, &mut *d_x_l);
1148 let mut d_x_u = nlp.x_u().make_new();
1149 nlp.px_u()
1150 .trans_mult_vector(-1.0, &*tmp_x, 0.0, &mut *d_x_u);
1151
1152 let mut tmp_s_l = nlp.d_l().make_new();
1153 tmp_s_l.set(1.0);
1154 let mut tmp_s_u = nlp.d_u().make_new();
1155 tmp_s_u.set(1.0);
1156 let mut tmp_s = self.curr_iv().s.make_new();
1157 nlp.pd_l().mult_vector(1.0, &*tmp_s_l, 0.0, &mut *tmp_s);
1158 nlp.pd_u().mult_vector(-1.0, &*tmp_s_u, 1.0, &mut *tmp_s);
1159 let mut d_s_l = nlp.d_l().make_new();
1160 nlp.pd_l().trans_mult_vector(1.0, &*tmp_s, 0.0, &mut *d_s_l);
1161 let mut d_s_u = nlp.d_u().make_new();
1162 nlp.pd_u()
1163 .trans_mult_vector(-1.0, &*tmp_s, 0.0, &mut *d_s_u);
1164
1165 DampingIndicators {
1166 x_l: rc_from(d_x_l),
1167 x_u: rc_from(d_x_u),
1168 s_l: rc_from(d_s_l),
1169 s_u: rc_from(d_s_u),
1170 }
1171 }
1172
1173 pub fn curr_grad_lag_with_damping_x(&self) -> Rc<dyn Vector> {
1178 if self.kappa_d == 0.0 {
1179 return self.curr_grad_lag_x();
1180 }
1181 let mu = self.data.borrow().curr_mu;
1182 let di = self.damping_indicators();
1183 let (d_x_l, d_x_u) = (di.x_l, di.x_u);
1184 let glx = self.curr_grad_lag_x();
1185 let mut tmp = glx.make_new();
1186 tmp.copy(&*glx);
1187 let nlp = self.nlp.borrow();
1188 nlp.px_l()
1189 .mult_vector(self.kappa_d * mu, &*d_x_l, 1.0, &mut *tmp);
1190 nlp.px_u()
1191 .mult_vector(-self.kappa_d * mu, &*d_x_u, 1.0, &mut *tmp);
1192 rc_from(tmp)
1193 }
1194
1195 pub fn curr_grad_lag_with_damping_s(&self) -> Rc<dyn Vector> {
1196 if self.kappa_d == 0.0 {
1197 return self.curr_grad_lag_s();
1198 }
1199 let mu = self.data.borrow().curr_mu;
1200 let di = self.damping_indicators();
1201 let (d_s_l, d_s_u) = (di.s_l, di.s_u);
1202 let gls = self.curr_grad_lag_s();
1203 let mut tmp = gls.make_new();
1204 tmp.copy(&*gls);
1205 let nlp = self.nlp.borrow();
1206 nlp.pd_l()
1207 .mult_vector(self.kappa_d * mu, &*d_s_l, 1.0, &mut *tmp);
1208 nlp.pd_u()
1209 .mult_vector(-self.kappa_d * mu, &*d_s_u, 1.0, &mut *tmp);
1210 rc_from(tmp)
1211 }
1212
1213 pub fn grad_kappa_times_damping_x(&self) -> Rc<dyn Vector> {
1220 let mut tmp = self.curr_iv().x.make_new();
1221 tmp.set(0.0);
1222 if self.kappa_d > 0.0 {
1223 let di = self.damping_indicators();
1224 let nlp = self.nlp.borrow();
1225 nlp.px_l()
1226 .mult_vector(self.kappa_d, &*di.x_l, 0.0, &mut *tmp);
1227 nlp.px_u()
1228 .mult_vector(-self.kappa_d, &*di.x_u, 1.0, &mut *tmp);
1229 }
1230 rc_from(tmp)
1231 }
1232
1233 pub fn grad_kappa_times_damping_s(&self) -> Rc<dyn Vector> {
1234 let mut tmp = self.curr_iv().s.make_new();
1235 tmp.set(0.0);
1236 if self.kappa_d > 0.0 {
1237 let di = self.damping_indicators();
1238 let nlp = self.nlp.borrow();
1239 nlp.pd_l()
1240 .mult_vector(self.kappa_d, &*di.s_l, 0.0, &mut *tmp);
1241 nlp.pd_u()
1242 .mult_vector(-self.kappa_d, &*di.s_u, 1.0, &mut *tmp);
1243 }
1244 rc_from(tmp)
1245 }
1246
1247 pub fn aff_step_alpha_primal_max(&self, delta_aff: &IteratesVector, tau: Number) -> Number {
1260 let nlp = self.nlp.borrow();
1261 let s_x_l = self.curr_slack_x_l();
1262 let s_x_u = self.curr_slack_x_u();
1263 let s_s_l = self.curr_slack_s_l();
1264 let s_s_u = self.curr_slack_s_u();
1265
1266 let mut step_x_l = s_x_l.make_new();
1268 nlp.px_l()
1269 .trans_mult_vector(1.0, &*delta_aff.x, 0.0, &mut *step_x_l);
1270 let mut step_x_u = s_x_u.make_new();
1271 nlp.px_u()
1272 .trans_mult_vector(-1.0, &*delta_aff.x, 0.0, &mut *step_x_u);
1273 let mut step_s_l = s_s_l.make_new();
1274 nlp.pd_l()
1275 .trans_mult_vector(1.0, &*delta_aff.s, 0.0, &mut *step_s_l);
1276 let mut step_s_u = s_s_u.make_new();
1277 nlp.pd_u()
1278 .trans_mult_vector(-1.0, &*delta_aff.s, 0.0, &mut *step_s_u);
1279
1280 s_x_l
1281 .frac_to_bound(&*step_x_l, tau)
1282 .min(s_x_u.frac_to_bound(&*step_x_u, tau))
1283 .min(s_s_l.frac_to_bound(&*step_s_l, tau))
1284 .min(s_s_u.frac_to_bound(&*step_s_u, tau))
1285 }
1286
1287 pub fn aff_step_alpha_dual_max(&self, delta_aff: &IteratesVector, tau: Number) -> Number {
1289 let iv = self.curr_iv();
1290 iv.z_l
1291 .frac_to_bound(&*delta_aff.z_l, tau)
1292 .min(iv.z_u.frac_to_bound(&*delta_aff.z_u, tau))
1293 .min(iv.v_l.frac_to_bound(&*delta_aff.v_l, tau))
1294 .min(iv.v_u.frac_to_bound(&*delta_aff.v_u, tau))
1295 }
1296
1297 pub fn aff_step_compl_avrg(
1301 &self,
1302 delta_aff: &IteratesVector,
1303 alpha_primal: Number,
1304 alpha_dual: Number,
1305 ) -> Number {
1306 let iv = self.curr_iv();
1307 let n = iv.z_l.dim() + iv.z_u.dim() + iv.v_l.dim() + iv.v_u.dim();
1308 if n == 0 {
1309 return 0.0;
1310 }
1311 let nlp = self.nlp.borrow();
1312
1313 let s_x_l = self.curr_slack_x_l();
1315 let mut s_x_l_aff = s_x_l.make_new();
1316 s_x_l_aff.copy(&*s_x_l);
1317 let mut tmp = s_x_l.make_new();
1318 nlp.px_l()
1319 .trans_mult_vector(1.0, &*delta_aff.x, 0.0, &mut *tmp);
1320 s_x_l_aff.axpy(alpha_primal, &*tmp);
1321 let mut z_l_aff = iv.z_l.make_new();
1323 z_l_aff.copy(&*iv.z_l);
1324 z_l_aff.axpy(alpha_dual, &*delta_aff.z_l);
1325 let mut acc = s_x_l_aff.dot(&*z_l_aff);
1326
1327 let s_x_u = self.curr_slack_x_u();
1329 let mut s_x_u_aff = s_x_u.make_new();
1330 s_x_u_aff.copy(&*s_x_u);
1331 let mut tmp = s_x_u.make_new();
1332 nlp.px_u()
1333 .trans_mult_vector(-1.0, &*delta_aff.x, 0.0, &mut *tmp);
1334 s_x_u_aff.axpy(alpha_primal, &*tmp);
1335 let mut z_u_aff = iv.z_u.make_new();
1336 z_u_aff.copy(&*iv.z_u);
1337 z_u_aff.axpy(alpha_dual, &*delta_aff.z_u);
1338 acc += s_x_u_aff.dot(&*z_u_aff);
1339
1340 let s_s_l = self.curr_slack_s_l();
1342 let mut s_s_l_aff = s_s_l.make_new();
1343 s_s_l_aff.copy(&*s_s_l);
1344 let mut tmp = s_s_l.make_new();
1345 nlp.pd_l()
1346 .trans_mult_vector(1.0, &*delta_aff.s, 0.0, &mut *tmp);
1347 s_s_l_aff.axpy(alpha_primal, &*tmp);
1348 let mut v_l_aff = iv.v_l.make_new();
1349 v_l_aff.copy(&*iv.v_l);
1350 v_l_aff.axpy(alpha_dual, &*delta_aff.v_l);
1351 acc += s_s_l_aff.dot(&*v_l_aff);
1352
1353 let s_s_u = self.curr_slack_s_u();
1355 let mut s_s_u_aff = s_s_u.make_new();
1356 s_s_u_aff.copy(&*s_s_u);
1357 let mut tmp = s_s_u.make_new();
1358 nlp.pd_u()
1359 .trans_mult_vector(-1.0, &*delta_aff.s, 0.0, &mut *tmp);
1360 s_s_u_aff.axpy(alpha_primal, &*tmp);
1361 let mut v_u_aff = iv.v_u.make_new();
1362 v_u_aff.copy(&*iv.v_u);
1363 v_u_aff.axpy(alpha_dual, &*delta_aff.v_u);
1364 acc += s_s_u_aff.dot(&*v_u_aff);
1365
1366 acc / Number::from(n)
1367 }
1368}
1369
1370pub type IpoptCqHandle = Rc<RefCell<IpoptCalculatedQuantities>>;
1372
1373struct DampingIndicators {
1377 x_l: Rc<dyn Vector>,
1378 x_u: Rc<dyn Vector>,
1379 s_l: Rc<dyn Vector>,
1380 s_u: Rc<dyn Vector>,
1381}
1382
1383#[cfg(test)]
1384mod tests {
1385 use super::*;
1386 use crate::ipopt_data::IpoptData;
1387 use crate::iterates_vector::IteratesVector;
1388 use pounce_common::types::Index;
1389 use pounce_linalg::dense_vector::{DenseVector, DenseVectorSpace};
1390 use pounce_linalg::expansion_matrix::{ExpansionMatrix, ExpansionMatrixSpace};
1391 use std::rc::Rc as StdRc;
1392
1393 fn dvec(values: &[Number]) -> DenseVector {
1394 let space = DenseVectorSpace::new(values.len() as Index);
1395 let mut v = space.make_new_dense();
1396 v.values_mut().copy_from_slice(values);
1397 v
1398 }
1399
1400 fn rcv(values: &[Number]) -> Rc<dyn Vector> {
1401 StdRc::new(dvec(values))
1402 }
1403
1404 struct MockNlp {
1410 x_l: DenseVector,
1411 x_u: DenseVector,
1412 d_l: DenseVector,
1413 d_u: DenseVector,
1414 px_l: Rc<dyn Matrix>,
1415 px_u: Rc<dyn Matrix>,
1416 pd_l: Rc<dyn Matrix>,
1417 pd_u: Rc<dyn Matrix>,
1418 }
1419
1420 impl MockNlp {
1421 fn new() -> Self {
1422 let x_l = dvec(&[0.0]);
1424 let x_u = dvec(&[5.0]);
1426 let d_l = dvec(&[1.0]);
1428 let d_u = dvec(&[]);
1429
1430 let px_l_space = ExpansionMatrixSpace::new(2, 1, &[0], 0);
1431 let px_u_space = ExpansionMatrixSpace::new(2, 1, &[1], 0);
1432 let pd_l_space = ExpansionMatrixSpace::new(1, 1, &[0], 0);
1433 let pd_u_space = ExpansionMatrixSpace::new(1, 0, &[], 0);
1434
1435 Self {
1436 x_l,
1437 x_u,
1438 d_l,
1439 d_u,
1440 px_l: StdRc::new(ExpansionMatrix::new(px_l_space)),
1441 px_u: StdRc::new(ExpansionMatrix::new(px_u_space)),
1442 pd_l: StdRc::new(ExpansionMatrix::new(pd_l_space)),
1443 pd_u: StdRc::new(ExpansionMatrix::new(pd_u_space)),
1444 }
1445 }
1446 }
1447
1448 impl crate::ipopt_nlp::Nlp for MockNlp {
1449 fn n(&self) -> Index {
1450 2
1451 }
1452 fn m_eq(&self) -> Index {
1453 1
1454 }
1455 fn m_ineq(&self) -> Index {
1456 1
1457 }
1458 fn eval_f(&mut self, x: &dyn Vector) -> Number {
1459 let xx = x.as_any().downcast_ref::<DenseVector>().unwrap();
1461 xx.values()[0] * xx.values()[0] + xx.values()[1] * xx.values()[1]
1462 }
1463 fn eval_grad_f(&mut self, x: &dyn Vector, g: &mut dyn Vector) {
1464 let xx = x.as_any().downcast_ref::<DenseVector>().unwrap();
1466 let gg = g.as_any_mut().downcast_mut::<DenseVector>().unwrap();
1467 gg.values_mut()[0] = 2.0 * xx.values()[0];
1468 gg.values_mut()[1] = 2.0 * xx.values()[1];
1469 }
1470 fn eval_c(&mut self, x: &dyn Vector, c: &mut dyn Vector) {
1471 let xx = x.as_any().downcast_ref::<DenseVector>().unwrap();
1472 let cc = c.as_any_mut().downcast_mut::<DenseVector>().unwrap();
1473 cc.values_mut()[0] = xx.values()[0] + xx.values()[1] - 1.0;
1474 }
1475 fn eval_d(&mut self, x: &dyn Vector, d: &mut dyn Vector) {
1476 let xx = x.as_any().downcast_ref::<DenseVector>().unwrap();
1477 let dd = d.as_any_mut().downcast_mut::<DenseVector>().unwrap();
1478 dd.values_mut()[0] = xx.values()[0];
1479 }
1480 fn eval_jac_c(&mut self, _x: &dyn Vector) -> Rc<dyn Matrix> {
1481 unimplemented!("not exercised in Phase 5 unit tests")
1482 }
1483 fn eval_jac_d(&mut self, _x: &dyn Vector) -> Rc<dyn Matrix> {
1484 unimplemented!("not exercised in Phase 5 unit tests")
1485 }
1486 fn eval_h(
1487 &mut self,
1488 _x: &dyn Vector,
1489 _obj_factor: Number,
1490 _y_c: &dyn Vector,
1491 _y_d: &dyn Vector,
1492 ) -> Rc<dyn SymMatrix> {
1493 unimplemented!()
1494 }
1495 }
1496
1497 impl IpoptNlp for MockNlp {
1498 fn x_l(&self) -> &dyn Vector {
1499 &self.x_l
1500 }
1501 fn x_u(&self) -> &dyn Vector {
1502 &self.x_u
1503 }
1504 fn d_l(&self) -> &dyn Vector {
1505 &self.d_l
1506 }
1507 fn d_u(&self) -> &dyn Vector {
1508 &self.d_u
1509 }
1510 fn px_l(&self) -> Rc<dyn Matrix> {
1511 self.px_l.clone()
1512 }
1513 fn px_u(&self) -> Rc<dyn Matrix> {
1514 self.px_u.clone()
1515 }
1516 fn pd_l(&self) -> Rc<dyn Matrix> {
1517 self.pd_l.clone()
1518 }
1519 fn pd_u(&self) -> Rc<dyn Matrix> {
1520 self.pd_u.clone()
1521 }
1522 }
1523
1524 fn fixture() -> IpoptCalculatedQuantities {
1525 let mut data = IpoptData::new();
1526 data.curr_mu = 0.1;
1527 let iv = IteratesVector::new(
1531 rcv(&[2.0, 3.0]),
1532 rcv(&[4.0]),
1533 rcv(&[1.0]),
1534 rcv(&[1.0]),
1535 rcv(&[0.5]),
1536 rcv(&[0.7]),
1537 rcv(&[0.3]),
1538 rcv(&[]),
1539 );
1540 data.set_curr(iv);
1541 let data_handle = StdRc::new(RefCell::new(data));
1542 let nlp: StdRc<RefCell<dyn IpoptNlp>> = StdRc::new(RefCell::new(MockNlp::new()));
1543 let mut cq = IpoptCalculatedQuantities::new(data_handle, nlp);
1544 cq.kappa_d = 0.0;
1546 cq
1547 }
1548
1549 fn dense_vals(v: &Rc<dyn Vector>) -> Vec<Number> {
1550 v.as_any()
1551 .downcast_ref::<DenseVector>()
1552 .unwrap()
1553 .values()
1554 .to_vec()
1555 }
1556
1557 #[test]
1558 fn slack_x_lower_is_x0_minus_x_l() {
1559 let cq = fixture();
1561 assert_eq!(dense_vals(&cq.curr_slack_x_l()), vec![2.0]);
1562 }
1563
1564 #[test]
1565 fn slack_x_upper_is_x_u_minus_x1() {
1566 let cq = fixture();
1568 assert_eq!(dense_vals(&cq.curr_slack_x_u()), vec![2.0]);
1569 }
1570
1571 #[test]
1572 fn slack_s_lower() {
1573 let cq = fixture();
1575 assert_eq!(dense_vals(&cq.curr_slack_s_l()), vec![3.0]);
1576 }
1577
1578 #[test]
1579 fn grad_f_is_twice_x() {
1580 let cq = fixture();
1581 assert_eq!(dense_vals(&cq.curr_grad_f()), vec![4.0, 6.0]);
1582 }
1583
1584 #[test]
1585 fn compl_x_l_is_slack_times_z() {
1586 let cq = fixture();
1588 assert_eq!(dense_vals(&cq.curr_compl_x_l()), vec![1.0]);
1589 }
1590
1591 #[test]
1592 fn relaxed_compl_x_l_subtracts_mu() {
1593 let cq = fixture();
1595 assert!((dense_vals(&cq.curr_relaxed_compl_x_l())[0] - 0.9).abs() < 1e-15);
1596 }
1597
1598 #[test]
1599 fn sigma_x_routes_z_over_slack_through_p() {
1600 let cq = fixture();
1604 let s = dense_vals(&cq.curr_sigma_x());
1605 assert!((s[0] - 0.25).abs() < 1e-15);
1606 assert!((s[1] - 0.35).abs() < 1e-15);
1607 }
1608
1609 #[test]
1610 fn sigma_s_lower_only() {
1611 let cq = fixture();
1613 let s = dense_vals(&cq.curr_sigma_s());
1614 assert!((s[0] - 0.1).abs() < 1e-15);
1615 }
1616
1617 #[test]
1618 fn avrg_compl_averages_over_active_bounds() {
1619 let cq = fixture();
1624 assert!((cq.curr_avrg_compl() - 1.1).abs() < 1e-15);
1625 }
1626
1627 #[test]
1628 fn complementarity_min_takes_min_over_active_pairs() {
1629 let cq = fixture();
1632 assert!((cq.curr_complementarity_min() - 0.9).abs() < 1e-15);
1633 }
1634
1635 #[test]
1636 fn centrality_measure_is_min_over_avrg() {
1637 let cq = fixture();
1639 let xi = cq.curr_centrality_measure();
1640 assert!((xi - 0.9 / 1.1).abs() < 1e-15);
1641 }
1642
1643 #[test]
1644 fn curr_f_evaluates_objective() {
1645 let cq = fixture();
1647 assert!((cq.curr_f() - 13.0).abs() < 1e-15);
1648 }
1649
1650 #[test]
1651 fn curr_barrier_obj_subtracts_mu_log_slacks() {
1652 let cq = fixture();
1656 let expected = 13.0 - 0.1 * (2.0 * 2.0_f64.ln() + 3.0_f64.ln());
1657 assert!((cq.curr_barrier_obj() - expected).abs() < 1e-13);
1658 }
1659
1660 #[test]
1661 fn curr_constraint_violation_is_one_norm() {
1662 let cq = fixture();
1666 assert!((cq.curr_constraint_violation() - 6.0).abs() < 1e-13);
1667 }
1668
1669 #[test]
1670 fn grad_barrier_obj_x_subtracts_mu_inv_slack() {
1671 let cq = fixture();
1676 let g = dense_vals(&cq.curr_grad_barrier_obj_x());
1677 assert!((g[0] - 3.95).abs() < 1e-13);
1678 assert!((g[1] - 6.05).abs() < 1e-13);
1679 }
1680
1681 #[test]
1682 fn grad_lag_s_is_minus_y_d_minus_pl_v_l_plus_pu_v_u() {
1683 let cq = fixture();
1687 assert!((dense_vals(&cq.curr_grad_lag_s())[0] + 1.3).abs() < 1e-15);
1688 }
1689
1690 fn zero_iv_like(iv: &IteratesVector) -> IteratesVector {
1691 IteratesVector::new(
1694 rcv(&vec![0.0; iv.x.dim() as usize]),
1695 rcv(&vec![0.0; iv.s.dim() as usize]),
1696 rcv(&vec![0.0; iv.y_c.dim() as usize]),
1697 rcv(&vec![0.0; iv.y_d.dim() as usize]),
1698 rcv(&vec![0.0; iv.z_l.dim() as usize]),
1699 rcv(&vec![0.0; iv.z_u.dim() as usize]),
1700 rcv(&vec![0.0; iv.v_l.dim() as usize]),
1701 rcv(&vec![0.0; iv.v_u.dim() as usize]),
1702 )
1703 }
1704
1705 #[test]
1706 fn aff_step_compl_avrg_with_zero_step_matches_curr_avrg_compl() {
1707 let cq = fixture();
1711 let iv = cq.curr_iv();
1712 let zero = zero_iv_like(&iv);
1713 let m = cq.aff_step_compl_avrg(&zero, 1.0, 1.0);
1714 assert!((m - 1.1).abs() < 1e-13);
1715 assert!((cq.curr_avrg_compl() - 1.1).abs() < 1e-13);
1716 }
1717
1718 #[test]
1719 fn aff_step_compl_avrg_responds_to_primal_step() {
1720 let cq = fixture();
1724 let iv = cq.curr_iv();
1725 let mut z = zero_iv_like(&iv);
1726 z.x = rcv(&[1.0, 0.0]);
1727 let m = cq.aff_step_compl_avrg(&z, 1.0, 1.0);
1728 assert!((m - 1.2666666666666666).abs() < 1e-13);
1729 }
1730
1731 #[test]
1732 fn aff_step_alpha_primal_truncates_to_x_lower_bound() {
1733 let cq = fixture();
1735 let iv = cq.curr_iv();
1736 let mut z = zero_iv_like(&iv);
1737 z.x = rcv(&[-3.0, 0.0]);
1738 let a = cq.aff_step_alpha_primal_max(&z, 1.0);
1739 assert!((a - 2.0 / 3.0).abs() < 1e-13);
1740 }
1741
1742 #[test]
1743 fn aff_step_alpha_dual_truncates_to_z_lower_bound() {
1744 let cq = fixture();
1746 let iv = cq.curr_iv();
1747 let mut z = zero_iv_like(&iv);
1748 z.z_l = rcv(&[-1.0]);
1749 let a = cq.aff_step_alpha_dual_max(&z, 1.0);
1750 assert!((a - 0.5).abs() < 1e-13);
1751 }
1752
1753 #[test]
1754 fn grad_barr_t_delta_dots_barrier_grads_with_step() {
1755 let cq = fixture();
1759 let dx = dvec(&[1.0, 2.0]);
1760 let ds = dvec(&[3.0]);
1761 let r = cq.curr_grad_barr_t_delta(&dx, &ds);
1762 let expected = 3.95 + 12.10 - 0.1;
1763 assert!((r - expected).abs() < 1e-13, "r = {r}");
1764 }
1765
1766 #[test]
1767 fn dwd_with_no_w_collapses_to_sigma_quadratic() {
1768 let cq = fixture();
1773 let dx = dvec(&[2.0, -1.0]);
1774 let ds = dvec(&[3.0]);
1775 let r = cq.curr_dwd(&dx, &ds);
1776 assert!((r - 2.25).abs() < 1e-13, "r = {r}");
1777 }
1778
1779 #[test]
1780 fn dwd_includes_pd_perturbations() {
1781 let cq = fixture();
1787 {
1788 let mut d = cq.data.borrow_mut();
1789 d.perturbations.delta_x = 0.5;
1790 d.perturbations.delta_s = 0.25;
1791 }
1792 let dx = dvec(&[2.0, -1.0]);
1793 let ds = dvec(&[3.0]);
1794 let r = cq.curr_dwd(&dx, &ds);
1795 assert!((r - 7.00).abs() < 1e-13, "r = {r}");
1796 }
1797}