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::dense_vector::DenseVector;
28use pounce_linalg::{Matrix, SymMatrix, Vector};
29use std::cell::RefCell;
30use std::rc::Rc;
31
32pub struct IpoptCalculatedQuantities {
35 data: IpoptDataHandle,
36 nlp: Rc<RefCell<dyn IpoptNlp>>,
37
38 pub s_max: Number,
40 pub kappa_d: Number,
43 pub slack_move: Number,
48
49 curr_slack_x_l_cache: RefCell<Cache<Rc<dyn Vector>>>,
58 curr_slack_x_u_cache: RefCell<Cache<Rc<dyn Vector>>>,
59 curr_slack_s_l_cache: RefCell<Cache<Rc<dyn Vector>>>,
60 curr_slack_s_u_cache: RefCell<Cache<Rc<dyn Vector>>>,
61 curr_sigma_x_cache: RefCell<Cache<Rc<dyn Vector>>>,
62 curr_sigma_s_cache: RefCell<Cache<Rc<dyn Vector>>>,
63}
64
65fn rc_from(v: Box<dyn Vector>) -> Rc<dyn Vector> {
68 Rc::from(v)
69}
70
71fn unscaled_block_amax(v: &dyn Vector, scale: Option<&[Number]>) -> Number {
78 let Some(s) = scale else {
79 return v.amax();
80 };
81 match v.as_any().downcast_ref::<DenseVector>() {
82 Some(d) => d
83 .values()
84 .iter()
85 .zip(s.iter())
86 .map(|(&x, &f)| if f == 0.0 { x.abs() } else { (x / f).abs() })
87 .fold(0.0, Number::max),
88 None => v.amax(),
89 }
90}
91
92pub struct AdjustedBounds {
96 pub adjusted: usize,
98 pub x_l: Box<dyn Vector>,
99 pub x_u: Box<dyn Vector>,
100 pub d_l: Box<dyn Vector>,
101 pub d_u: Box<dyn Vector>,
102}
103
104impl IpoptCalculatedQuantities {
105 pub fn new(data: IpoptDataHandle, nlp: Rc<RefCell<dyn IpoptNlp>>) -> Self {
106 Self {
107 data,
108 nlp,
109 s_max: 100.0,
110 kappa_d: 1e-5,
111 slack_move: f64::EPSILON.powf(0.75),
112 curr_slack_x_l_cache: RefCell::new(Cache::new(1)),
113 curr_slack_x_u_cache: RefCell::new(Cache::new(1)),
114 curr_slack_s_l_cache: RefCell::new(Cache::new(1)),
115 curr_slack_s_u_cache: RefCell::new(Cache::new(1)),
116 curr_sigma_x_cache: RefCell::new(Cache::new(1)),
117 curr_sigma_s_cache: RefCell::new(Cache::new(1)),
118 }
119 }
120
121 pub fn data(&self) -> &IpoptDataHandle {
122 &self.data
123 }
124
125 pub fn nlp(&self) -> &Rc<RefCell<dyn IpoptNlp>> {
126 &self.nlp
127 }
128
129 pub(crate) fn curr_iv(&self) -> IteratesVector {
130 let Some(iv) = self.data.borrow().curr.as_ref().cloned() else {
131 unreachable!("IpoptCalculatedQuantities: curr iterate not set");
132 };
133 iv
134 }
135
136 fn trial_iv(&self) -> IteratesVector {
137 let Some(iv) = self.data.borrow().trial.as_ref().cloned() else {
138 unreachable!("IpoptCalculatedQuantities: trial iterate not set");
139 };
140 iv
141 }
142
143 fn calc_slack_l_box(p: &dyn Matrix, x: &dyn Vector, x_bound: &dyn Vector) -> Box<dyn Vector> {
150 let mut result = x_bound.make_new();
151 result.copy(x_bound);
152 p.trans_mult_vector(1.0, x, -1.0, &mut *result);
154 result
155 }
156
157 fn calc_slack_u_box(p: &dyn Matrix, x: &dyn Vector, x_bound: &dyn Vector) -> Box<dyn Vector> {
158 let mut result = x_bound.make_new();
159 result.copy(x_bound);
160 p.trans_mult_vector(-1.0, x, 1.0, &mut *result);
162 result
163 }
164
165 fn calculate_safe_slack(
174 &self,
175 slack: &mut dyn Vector,
176 bound: &dyn Vector,
177 multiplier: &dyn Vector,
178 mu: Number,
179 ) -> usize {
180 if slack.dim() == 0 {
181 return 0;
182 }
183 let min_slack = slack.min();
184 let mut s_min = f64::EPSILON * mu.min(1.0);
188 if s_min == 0.0 {
189 s_min = f64::MIN_POSITIVE;
190 }
191 if min_slack >= s_min {
192 return 0;
193 }
194
195 let mut t = slack.make_new();
198 t.copy(&*slack);
199 t.add_scalar(-s_min);
200 t.element_wise_sgn();
201 let mut zero_vec = t.make_new();
202 zero_vec.set(0.0);
203 t.element_wise_min(&*zero_vec); t.scal(-1.0); let retval = t.asum().round() as usize;
206
207 slack.element_wise_max(&*zero_vec);
210
211 let mut t2 = t.make_new();
213 let mut s_min_vec = t2.make_new();
214 s_min_vec.set(s_min);
215 if mu != 0.0 {
216 t2.set(mu);
218 t2.element_wise_divide(multiplier);
219 t2.element_wise_max(&*s_min_vec);
220 } else {
221 t2.copy(&*s_min_vec);
225 }
226 t2.axpy(-1.0, &*slack);
227
228 t.element_wise_select(&*t2);
230 t.axpy(1.0, &*slack);
231
232 let mut t_max = t2; t_max.set(1.0);
235 let mut abs_bound = bound.make_new();
236 abs_bound.copy(bound);
237 abs_bound.element_wise_abs();
238 t_max.element_wise_max(&*abs_bound);
239 t_max.add_one_vector(1.0, &*slack, self.slack_move);
241
242 t.element_wise_min(&*t_max);
244 slack.copy(&*t);
245 retval
246 }
247
248 fn safe_slack_l(
252 &self,
253 p: &dyn Matrix,
254 x: &dyn Vector,
255 bound: &dyn Vector,
256 multiplier: &dyn Vector,
257 ) -> (Rc<dyn Vector>, usize) {
258 let mu = self.data.borrow().curr_mu;
259 let mut result = Self::calc_slack_l_box(p, x, bound);
260 let n = self.calculate_safe_slack(&mut *result, bound, multiplier, mu);
261 (rc_from(result), n)
262 }
263
264 fn safe_slack_u(
265 &self,
266 p: &dyn Matrix,
267 x: &dyn Vector,
268 bound: &dyn Vector,
269 multiplier: &dyn Vector,
270 ) -> (Rc<dyn Vector>, usize) {
271 let mu = self.data.borrow().curr_mu;
272 let mut result = Self::calc_slack_u_box(p, x, bound);
273 let n = self.calculate_safe_slack(&mut *result, bound, multiplier, mu);
274 (rc_from(result), n)
275 }
276
277 pub fn curr_slack_x_l(&self) -> Rc<dyn Vector> {
278 let iv = self.curr_iv();
279 {
280 let cache = self.curr_slack_x_l_cache.borrow();
281 if let Some(v) = cache.get(&[iv.x.as_tagged()], &[]) {
282 return v;
283 }
284 }
285 let nlp = self.nlp.borrow();
286 let (v, _) = self.safe_slack_l(&*nlp.px_l(), &*iv.x, nlp.x_l(), &*iv.z_l);
287 self.curr_slack_x_l_cache
288 .borrow_mut()
289 .add(v.clone(), &[iv.x.as_tagged()], &[]);
290 v
291 }
292
293 pub fn curr_slack_x_u(&self) -> Rc<dyn Vector> {
294 let iv = self.curr_iv();
295 {
296 let cache = self.curr_slack_x_u_cache.borrow();
297 if let Some(v) = cache.get(&[iv.x.as_tagged()], &[]) {
298 return v;
299 }
300 }
301 let nlp = self.nlp.borrow();
302 let (v, _) = self.safe_slack_u(&*nlp.px_u(), &*iv.x, nlp.x_u(), &*iv.z_u);
303 self.curr_slack_x_u_cache
304 .borrow_mut()
305 .add(v.clone(), &[iv.x.as_tagged()], &[]);
306 v
307 }
308
309 pub fn curr_slack_s_l(&self) -> Rc<dyn Vector> {
310 let iv = self.curr_iv();
311 {
312 let cache = self.curr_slack_s_l_cache.borrow();
313 if let Some(v) = cache.get(&[iv.s.as_tagged()], &[]) {
314 return v;
315 }
316 }
317 let nlp = self.nlp.borrow();
318 let (v, _) = self.safe_slack_l(&*nlp.pd_l(), &*iv.s, nlp.d_l(), &*iv.v_l);
319 self.curr_slack_s_l_cache
320 .borrow_mut()
321 .add(v.clone(), &[iv.s.as_tagged()], &[]);
322 v
323 }
324
325 pub fn curr_slack_s_u(&self) -> Rc<dyn Vector> {
326 let iv = self.curr_iv();
327 {
328 let cache = self.curr_slack_s_u_cache.borrow();
329 if let Some(v) = cache.get(&[iv.s.as_tagged()], &[]) {
330 return v;
331 }
332 }
333 let nlp = self.nlp.borrow();
334 let (v, _) = self.safe_slack_u(&*nlp.pd_u(), &*iv.s, nlp.d_u(), &*iv.v_u);
335 self.curr_slack_s_u_cache
336 .borrow_mut()
337 .add(v.clone(), &[iv.s.as_tagged()], &[]);
338 v
339 }
340
341 pub fn trial_slack_x_l(&self) -> Rc<dyn Vector> {
342 let iv = self.trial_iv();
343 let mult = self.curr_iv();
344 let nlp = self.nlp.borrow();
345 self.safe_slack_l(&*nlp.px_l(), &*iv.x, nlp.x_l(), &*mult.z_l)
346 .0
347 }
348
349 pub fn trial_slack_x_u(&self) -> Rc<dyn Vector> {
350 let iv = self.trial_iv();
351 let mult = self.curr_iv();
352 let nlp = self.nlp.borrow();
353 self.safe_slack_u(&*nlp.px_u(), &*iv.x, nlp.x_u(), &*mult.z_u)
354 .0
355 }
356
357 pub fn trial_slack_s_l(&self) -> Rc<dyn Vector> {
358 let iv = self.trial_iv();
359 let mult = self.curr_iv();
360 let nlp = self.nlp.borrow();
361 self.safe_slack_l(&*nlp.pd_l(), &*iv.s, nlp.d_l(), &*mult.v_l)
362 .0
363 }
364
365 pub fn trial_slack_s_u(&self) -> Rc<dyn Vector> {
366 let iv = self.trial_iv();
367 let mult = self.curr_iv();
368 let nlp = self.nlp.borrow();
369 self.safe_slack_u(&*nlp.pd_u(), &*iv.s, nlp.d_u(), &*mult.v_u)
370 .0
371 }
372
373 pub fn adjusted_trial_bounds(&self) -> Option<AdjustedBounds> {
381 let iv = self.trial_iv();
382 let mult = self.curr_iv();
383 let nlp = self.nlp.borrow();
384
385 let (s_x_l, n_x_l) = self.safe_slack_l(&*nlp.px_l(), &*iv.x, nlp.x_l(), &*mult.z_l);
386 let (s_x_u, n_x_u) = self.safe_slack_u(&*nlp.px_u(), &*iv.x, nlp.x_u(), &*mult.z_u);
387 let (s_s_l, n_s_l) = self.safe_slack_l(&*nlp.pd_l(), &*iv.s, nlp.d_l(), &*mult.v_l);
388 let (s_s_u, n_s_u) = self.safe_slack_u(&*nlp.pd_u(), &*iv.s, nlp.d_u(), &*mult.v_u);
389
390 let adjusted = n_x_l + n_x_u + n_s_l + n_s_u;
391 if adjusted == 0 {
392 return None;
393 }
394
395 let mut new_x_l = nlp.x_l().make_new();
397 nlp.px_l()
398 .trans_mult_vector(1.0, &*iv.x, 0.0, &mut *new_x_l);
399 new_x_l.axpy(-1.0, &*s_x_l);
400 let mut new_x_u = nlp.x_u().make_new();
402 nlp.px_u()
403 .trans_mult_vector(1.0, &*iv.x, 0.0, &mut *new_x_u);
404 new_x_u.axpy(1.0, &*s_x_u);
405 let mut new_d_l = nlp.d_l().make_new();
407 nlp.pd_l()
408 .trans_mult_vector(1.0, &*iv.s, 0.0, &mut *new_d_l);
409 new_d_l.axpy(-1.0, &*s_s_l);
410 let mut new_d_u = nlp.d_u().make_new();
412 nlp.pd_u()
413 .trans_mult_vector(1.0, &*iv.s, 0.0, &mut *new_d_u);
414 new_d_u.axpy(1.0, &*s_s_u);
415
416 Some(AdjustedBounds {
417 adjusted,
418 x_l: new_x_l,
419 x_u: new_x_u,
420 d_l: new_d_l,
421 d_u: new_d_u,
422 })
423 }
424
425 pub fn curr_grad_f(&self) -> Rc<dyn Vector> {
430 let iv = self.curr_iv();
431 let mut nlp = self.nlp.borrow_mut();
432 let mut g = iv.x.make_new();
433 nlp.eval_grad_f(&*iv.x, &mut *g);
434 rc_from(g)
435 }
436
437 pub fn trial_grad_f(&self) -> Rc<dyn Vector> {
438 let iv = self.trial_iv();
439 let mut nlp = self.nlp.borrow_mut();
440 let mut g = iv.x.make_new();
441 nlp.eval_grad_f(&*iv.x, &mut *g);
442 rc_from(g)
443 }
444
445 pub fn curr_c(&self) -> Rc<dyn Vector> {
446 let iv = self.curr_iv();
447 let m = self.nlp.borrow().m_eq();
448 let mut nlp = self.nlp.borrow_mut();
449 let mut c = iv.y_c.make_new();
450 debug_assert_eq!(c.dim(), m);
451 nlp.eval_c(&*iv.x, &mut *c);
452 rc_from(c)
453 }
454
455 pub fn trial_c(&self) -> Rc<dyn Vector> {
456 let iv = self.trial_iv();
457 let mut nlp = self.nlp.borrow_mut();
458 let mut c = iv.y_c.make_new();
459 nlp.eval_c(&*iv.x, &mut *c);
460 rc_from(c)
461 }
462
463 pub fn curr_d(&self) -> Rc<dyn Vector> {
464 let iv = self.curr_iv();
465 let mut nlp = self.nlp.borrow_mut();
466 let mut d = iv.s.make_new();
467 nlp.eval_d(&*iv.x, &mut *d);
468 rc_from(d)
469 }
470
471 pub fn trial_d(&self) -> Rc<dyn Vector> {
472 let iv = self.trial_iv();
473 let mut nlp = self.nlp.borrow_mut();
474 let mut d = iv.s.make_new();
475 nlp.eval_d(&*iv.x, &mut *d);
476 rc_from(d)
477 }
478
479 pub fn curr_jac_c(&self) -> Rc<dyn Matrix> {
480 let iv = self.curr_iv();
481 self.nlp.borrow_mut().eval_jac_c(&*iv.x)
482 }
483
484 pub fn curr_jac_d(&self) -> Rc<dyn Matrix> {
485 let iv = self.curr_iv();
486 self.nlp.borrow_mut().eval_jac_d(&*iv.x)
487 }
488
489 pub fn curr_exact_hessian(&self) -> Rc<dyn SymMatrix> {
490 let iv = self.curr_iv();
491 self.nlp
492 .borrow_mut()
493 .eval_h(&*iv.x, 1.0, &*iv.y_c, &*iv.y_d)
494 }
495
496 pub fn curr_d_minus_s(&self) -> Rc<dyn Vector> {
498 let iv = self.curr_iv();
499 let d = self.curr_d();
500 let mut tmp = iv.s.make_new();
501 tmp.add_two_vectors(1.0, &*d, -1.0, &*iv.s, 0.0);
503 rc_from(tmp)
504 }
505
506 pub fn trial_d_minus_s(&self) -> Rc<dyn Vector> {
507 let iv = self.trial_iv();
508 let d = self.trial_d();
509 let mut tmp = iv.s.make_new();
510 tmp.add_two_vectors(1.0, &*d, -1.0, &*iv.s, 0.0);
511 rc_from(tmp)
512 }
513
514 pub fn curr_jac_c_t_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector> {
517 let iv = self.curr_iv();
518 let jac_c = self.curr_jac_c();
519 let mut tmp = iv.x.make_new();
520 jac_c.trans_mult_vector(1.0, vec, 0.0, &mut *tmp);
521 rc_from(tmp)
522 }
523
524 pub fn curr_jac_d_t_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector> {
526 let iv = self.curr_iv();
527 let jac_d = self.curr_jac_d();
528 let mut tmp = iv.x.make_new();
529 jac_d.trans_mult_vector(1.0, vec, 0.0, &mut *tmp);
530 rc_from(tmp)
531 }
532
533 pub fn curr_jac_c_t_times_curr_y_c(&self) -> Rc<dyn Vector> {
534 let iv = self.curr_iv();
535 self.curr_jac_c_t_times_vec(&*iv.y_c)
536 }
537
538 pub fn curr_jac_d_t_times_curr_y_d(&self) -> Rc<dyn Vector> {
539 let iv = self.curr_iv();
540 self.curr_jac_d_t_times_vec(&*iv.y_d)
541 }
542
543 pub fn curr_jac_c_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector> {
545 let iv = self.curr_iv();
546 let jac_c = self.curr_jac_c();
547 let mut tmp = iv.y_c.make_new();
548 jac_c.mult_vector(1.0, vec, 0.0, &mut *tmp);
549 rc_from(tmp)
550 }
551
552 pub fn curr_jac_d_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector> {
554 let iv = self.curr_iv();
555 let jac_d = self.curr_jac_d();
556 let mut tmp = iv.s.make_new();
557 jac_d.mult_vector(1.0, vec, 0.0, &mut *tmp);
558 rc_from(tmp)
559 }
560
561 pub fn curr_grad_lag_x(&self) -> Rc<dyn Vector> {
568 let iv = self.curr_iv();
569 let grad_f = self.curr_grad_f();
570 let jc_t_y_c = self.curr_jac_c_t_times_curr_y_c();
571 let jd_t_y_d = self.curr_jac_d_t_times_curr_y_d();
572
573 let mut tmp = iv.x.make_new();
574 tmp.copy(&*grad_f);
575 tmp.add_two_vectors(1.0, &*jc_t_y_c, 1.0, &*jd_t_y_d, 1.0);
576
577 let nlp = self.nlp.borrow();
578 nlp.px_l().mult_vector(-1.0, &*iv.z_l, 1.0, &mut *tmp);
579 nlp.px_u().mult_vector(1.0, &*iv.z_u, 1.0, &mut *tmp);
580 rc_from(tmp)
581 }
582
583 pub fn curr_grad_lag_s(&self) -> Rc<dyn Vector> {
586 let iv = self.curr_iv();
587 let mut tmp = iv.y_d.make_new();
588 let nlp = self.nlp.borrow();
589 nlp.pd_u().mult_vector(1.0, &*iv.v_u, 0.0, &mut *tmp);
591 nlp.pd_l().mult_vector(-1.0, &*iv.v_l, 1.0, &mut *tmp);
593 tmp.axpy(-1.0, &*iv.y_d);
595 rc_from(tmp)
596 }
597
598 fn calc_compl(slack: &dyn Vector, mult: &dyn Vector) -> Rc<dyn Vector> {
603 let mut result = slack.make_new();
604 result.copy(slack);
605 result.element_wise_multiply(mult);
606 rc_from(result)
607 }
608
609 pub fn curr_compl_x_l(&self) -> Rc<dyn Vector> {
610 let slack = self.curr_slack_x_l();
611 let z_l = self.curr_iv().z_l;
612 Self::calc_compl(&*slack, &*z_l)
613 }
614
615 pub fn curr_compl_x_u(&self) -> Rc<dyn Vector> {
616 let slack = self.curr_slack_x_u();
617 let z_u = self.curr_iv().z_u;
618 Self::calc_compl(&*slack, &*z_u)
619 }
620
621 pub fn curr_compl_s_l(&self) -> Rc<dyn Vector> {
622 let slack = self.curr_slack_s_l();
623 let v_l = self.curr_iv().v_l;
624 Self::calc_compl(&*slack, &*v_l)
625 }
626
627 pub fn curr_compl_s_u(&self) -> Rc<dyn Vector> {
628 let slack = self.curr_slack_s_u();
629 let v_u = self.curr_iv().v_u;
630 Self::calc_compl(&*slack, &*v_u)
631 }
632
633 pub fn curr_relaxed_compl_x_l(&self) -> Rc<dyn Vector> {
636 let mu = self.data.borrow().curr_mu;
637 let mut r = self.curr_compl_x_l().make_new();
638 r.copy(&*self.curr_compl_x_l());
639 r.add_scalar(-mu);
640 rc_from(r)
641 }
642
643 pub fn curr_relaxed_compl_x_u(&self) -> Rc<dyn Vector> {
644 let mu = self.data.borrow().curr_mu;
645 let mut r = self.curr_compl_x_u().make_new();
646 r.copy(&*self.curr_compl_x_u());
647 r.add_scalar(-mu);
648 rc_from(r)
649 }
650
651 pub fn curr_relaxed_compl_s_l(&self) -> Rc<dyn Vector> {
652 let mu = self.data.borrow().curr_mu;
653 let mut r = self.curr_compl_s_l().make_new();
654 r.copy(&*self.curr_compl_s_l());
655 r.add_scalar(-mu);
656 rc_from(r)
657 }
658
659 pub fn curr_relaxed_compl_s_u(&self) -> Rc<dyn Vector> {
660 let mu = self.data.borrow().curr_mu;
661 let mut r = self.curr_compl_s_u().make_new();
662 r.copy(&*self.curr_compl_s_u());
663 r.add_scalar(-mu);
664 rc_from(r)
665 }
666
667 pub fn curr_sigma_x(&self) -> Rc<dyn Vector> {
676 let iv = self.curr_iv();
677 {
678 let cache = self.curr_sigma_x_cache.borrow();
679 if let Some(v) = cache.get(
680 &[iv.x.as_tagged(), iv.z_l.as_tagged(), iv.z_u.as_tagged()],
681 &[],
682 ) {
683 return v;
684 }
685 }
686 let slack_l = self.curr_slack_x_l();
687 let slack_u = self.curr_slack_x_u();
688
689 let mut sigma = iv.x.make_new();
690 sigma.set(0.0);
691
692 let nlp = self.nlp.borrow();
693 nlp.px_l()
694 .add_m_sinv_z(1.0, &*slack_l, &*iv.z_l, &mut *sigma);
695 nlp.px_u()
696 .add_m_sinv_z(1.0, &*slack_u, &*iv.z_u, &mut *sigma);
697 let v = rc_from(sigma);
698 self.curr_sigma_x_cache.borrow_mut().add(
699 v.clone(),
700 &[iv.x.as_tagged(), iv.z_l.as_tagged(), iv.z_u.as_tagged()],
701 &[],
702 );
703 v
704 }
705
706 pub fn curr_sigma_s(&self) -> Rc<dyn Vector> {
707 let iv = self.curr_iv();
708 {
709 let cache = self.curr_sigma_s_cache.borrow();
710 if let Some(v) = cache.get(
711 &[iv.s.as_tagged(), iv.v_l.as_tagged(), iv.v_u.as_tagged()],
712 &[],
713 ) {
714 return v;
715 }
716 }
717 let slack_l = self.curr_slack_s_l();
718 let slack_u = self.curr_slack_s_u();
719
720 let mut sigma = iv.s.make_new();
721 sigma.set(0.0);
722
723 let nlp = self.nlp.borrow();
724 nlp.pd_l()
725 .add_m_sinv_z(1.0, &*slack_l, &*iv.v_l, &mut *sigma);
726 nlp.pd_u()
727 .add_m_sinv_z(1.0, &*slack_u, &*iv.v_u, &mut *sigma);
728 let v = rc_from(sigma);
729 self.curr_sigma_s_cache.borrow_mut().add(
730 v.clone(),
731 &[iv.s.as_tagged(), iv.v_l.as_tagged(), iv.v_u.as_tagged()],
732 &[],
733 );
734 v
735 }
736
737 pub fn curr_f(&self) -> Number {
754 let iv = self.curr_iv();
755 let mut nlp = self.nlp.borrow_mut();
756 nlp.eval_f(&*iv.x)
757 }
758
759 pub fn unscaled_curr_f(&self) -> Number {
766 let scaled = self.curr_f();
767 let factor = self.nlp.borrow().obj_scaling_factor();
768 if factor == 0.0 {
769 scaled
770 } else {
771 scaled / factor
772 }
773 }
774
775 pub fn curr_unscaled_dual_infeasibility_max(&self) -> Number {
784 let df = self.nlp.borrow().obj_scaling_factor();
785 let scaled = self.curr_dual_infeasibility_max();
786 if df == 0.0 || df == 1.0 {
787 scaled
788 } else {
789 scaled / df
790 }
791 }
792
793 pub fn curr_unscaled_complementarity_max(&self) -> Number {
799 let df = self.nlp.borrow().obj_scaling_factor();
800 let scaled = self.curr_complementarity_max();
801 if df == 0.0 || df == 1.0 {
802 scaled
803 } else {
804 scaled / df
805 }
806 }
807
808 pub fn curr_unscaled_primal_infeasibility_max(&self) -> Number {
815 let (dc, dd) = {
816 let nlp = self.nlp.borrow();
817 (nlp.c_scale_vec(), nlp.d_scale_vec())
818 };
819 if dc.is_none() && dd.is_none() {
820 return self.curr_primal_infeasibility_max();
821 }
822 let c_max = unscaled_block_amax(&*self.curr_c(), dc.as_deref());
823 let dms_max = unscaled_block_amax(&*self.curr_d_minus_s(), dd.as_deref());
824 c_max.max(dms_max)
825 }
826
827 pub fn curr_unscaled_nlp_error(&self) -> Number {
835 self.curr_unscaled_dual_infeasibility_max()
836 .max(self.curr_unscaled_primal_infeasibility_max())
837 .max(self.curr_unscaled_complementarity_max())
838 }
839
840 pub fn trial_f(&self) -> Number {
841 let iv = self.trial_iv();
842 let mut nlp = self.nlp.borrow_mut();
843 nlp.eval_f(&*iv.x)
844 }
845
846 fn barrier_obj_at(
847 &self,
848 f: Number,
849 s_x_l: &dyn Vector,
850 s_x_u: &dyn Vector,
851 s_s_l: &dyn Vector,
852 s_s_u: &dyn Vector,
853 ) -> Number {
854 let mu = self.data.borrow().curr_mu;
855 let log_sum = s_x_l.sum_logs() + s_x_u.sum_logs() + s_s_l.sum_logs() + s_s_u.sum_logs();
856 let mut phi = f - mu * log_sum;
857 if self.kappa_d > 0.0 {
858 let di = self.damping_indicators();
859 phi += self.kappa_d * mu * s_x_l.dot(&*di.x_l);
860 phi += self.kappa_d * mu * s_x_u.dot(&*di.x_u);
861 phi += self.kappa_d * mu * s_s_l.dot(&*di.s_l);
862 phi += self.kappa_d * mu * s_s_u.dot(&*di.s_u);
863 }
864 phi
865 }
866
867 pub fn curr_barrier_obj(&self) -> Number {
868 let f = self.curr_f();
869 let s_x_l = self.curr_slack_x_l();
870 let s_x_u = self.curr_slack_x_u();
871 let s_s_l = self.curr_slack_s_l();
872 let s_s_u = self.curr_slack_s_u();
873 self.barrier_obj_at(f, &*s_x_l, &*s_x_u, &*s_s_l, &*s_s_u)
874 }
875
876 pub fn trial_barrier_obj(&self) -> Number {
877 let f = self.trial_f();
878 let s_x_l = self.trial_slack_x_l();
879 let s_x_u = self.trial_slack_x_u();
880 let s_s_l = self.trial_slack_s_l();
881 let s_s_u = self.trial_slack_s_u();
882 self.barrier_obj_at(f, &*s_x_l, &*s_x_u, &*s_s_l, &*s_s_u)
883 }
884
885 pub fn curr_grad_barrier_obj_x(&self) -> Rc<dyn Vector> {
889 let iv = self.curr_iv();
890 let mu = self.data.borrow().curr_mu;
891 let s_l = self.curr_slack_x_l();
892 let s_u = self.curr_slack_x_u();
893
894 let mut inv_s_l = s_l.make_new();
895 inv_s_l.copy(&*s_l);
896 inv_s_l.element_wise_reciprocal();
897 let mut inv_s_u = s_u.make_new();
898 inv_s_u.copy(&*s_u);
899 inv_s_u.element_wise_reciprocal();
900
901 let grad_f = self.curr_grad_f();
902 let mut tmp = iv.x.make_new();
903 tmp.copy(&*grad_f);
904 let nlp = self.nlp.borrow();
905 nlp.px_l().mult_vector(-mu, &*inv_s_l, 1.0, &mut *tmp);
907 nlp.px_u().mult_vector(mu, &*inv_s_u, 1.0, &mut *tmp);
909
910 if self.kappa_d > 0.0 {
911 let di = self.damping_indicators();
912 nlp.px_l()
914 .mult_vector(self.kappa_d * mu, &*di.x_l, 1.0, &mut *tmp);
915 nlp.px_u()
917 .mult_vector(-self.kappa_d * mu, &*di.x_u, 1.0, &mut *tmp);
918 }
919 rc_from(tmp)
920 }
921
922 pub fn curr_grad_barrier_obj_s(&self) -> Rc<dyn Vector> {
925 let iv = self.curr_iv();
926 let mu = self.data.borrow().curr_mu;
927 let s_l = self.curr_slack_s_l();
928 let s_u = self.curr_slack_s_u();
929
930 let mut inv_s_l = s_l.make_new();
931 inv_s_l.copy(&*s_l);
932 inv_s_l.element_wise_reciprocal();
933 let mut inv_s_u = s_u.make_new();
934 inv_s_u.copy(&*s_u);
935 inv_s_u.element_wise_reciprocal();
936
937 let mut tmp = iv.s.make_new();
938 tmp.set(0.0);
939 let nlp = self.nlp.borrow();
940 nlp.pd_l().mult_vector(-mu, &*inv_s_l, 1.0, &mut *tmp);
941 nlp.pd_u().mult_vector(mu, &*inv_s_u, 1.0, &mut *tmp);
942
943 if self.kappa_d > 0.0 {
944 let di = self.damping_indicators();
945 nlp.pd_l()
946 .mult_vector(self.kappa_d * mu, &*di.s_l, 1.0, &mut *tmp);
947 nlp.pd_u()
948 .mult_vector(-self.kappa_d * mu, &*di.s_u, 1.0, &mut *tmp);
949 }
950 rc_from(tmp)
951 }
952
953 pub fn curr_grad_barr_t_delta(&self, delta_x: &dyn Vector, delta_s: &dyn Vector) -> Number {
965 let g_x = self.curr_grad_barrier_obj_x();
966 let g_s = self.curr_grad_barrier_obj_s();
967 g_x.dot(delta_x) + g_s.dot(delta_s)
968 }
969
970 pub fn curr_dwd(&self, delta_x: &dyn Vector, delta_s: &dyn Vector) -> Number {
977 let mut dwd: Number = 0.0;
978
979 if let Some(w) = self.data.borrow().w.clone() {
981 let mut wd = delta_x.make_new();
982 w.mult_vector(1.0, delta_x, 0.0, &mut *wd);
983 dwd += wd.dot(delta_x);
984 }
985
986 let sigma_x = self.curr_sigma_x();
988 let mut tmp_x = delta_x.make_new();
989 tmp_x.copy(delta_x);
990 tmp_x.element_wise_multiply(&*sigma_x);
991 dwd += tmp_x.dot(delta_x);
992
993 let sigma_s = self.curr_sigma_s();
995 let mut tmp_s = delta_s.make_new();
996 tmp_s.copy(delta_s);
997 tmp_s.element_wise_multiply(&*sigma_s);
998 dwd += tmp_s.dot(delta_s);
999
1000 let pert = self.data.borrow().perturbations;
1002 if pert.delta_x != 0.0 {
1003 let nx = delta_x.nrm2();
1004 dwd += pert.delta_x * nx * nx;
1005 }
1006 if pert.delta_s != 0.0 {
1007 let ns = delta_s.nrm2();
1008 dwd += pert.delta_s * ns * ns;
1009 }
1010
1011 dwd.max(0.0)
1012 }
1013
1014 pub fn curr_constraint_violation(&self) -> Number {
1022 let c = self.curr_c();
1023 let dms = self.curr_d_minus_s();
1024 c.asum() + dms.asum()
1025 }
1026
1027 pub fn trial_constraint_violation(&self) -> Number {
1028 let c = self.trial_c();
1029 let dms = self.trial_d_minus_s();
1030 c.asum() + dms.asum()
1031 }
1032
1033 pub fn curr_primal_infeasibility_max(&self) -> Number {
1038 let c = self.curr_c();
1039 let dms = self.curr_d_minus_s();
1040 c.amax().max(dms.amax())
1041 }
1042
1043 pub fn curr_dual_infeasibility_max(&self) -> Number {
1046 let glx = self.curr_grad_lag_x();
1047 let gls = self.curr_grad_lag_s();
1048 glx.amax().max(gls.amax())
1049 }
1050
1051 pub fn curr_infeasibility_stationarity(&self) -> Number {
1061 let c = self.curr_c();
1062 let dms = self.curr_d_minus_s();
1063 let jc_t_c = self.curr_jac_c_t_times_vec(&*c);
1064 let jd_t_dms = self.curr_jac_d_t_times_vec(&*dms);
1065 let mut grad = jc_t_c.make_new();
1066 grad.add_two_vectors(1.0, &*jc_t_c, 1.0, &*jd_t_dms, 0.0);
1067 let viol = c.amax().max(dms.amax());
1068 grad.amax() / viol.max(1.0)
1069 }
1070
1071 pub fn curr_avrg_compl(&self) -> Number {
1079 let iv = self.curr_iv();
1080 let n = iv.z_l.dim() + iv.z_u.dim() + iv.v_l.dim() + iv.v_u.dim();
1081 if n == 0 {
1082 return 0.0;
1083 }
1084 let s_x_l = self.curr_slack_x_l();
1085 let s_x_u = self.curr_slack_x_u();
1086 let s_s_l = self.curr_slack_s_l();
1087 let s_s_u = self.curr_slack_s_u();
1088 let mut acc = iv.z_l.dot(&*s_x_l);
1089 acc += iv.z_u.dot(&*s_x_u);
1090 acc += iv.v_l.dot(&*s_s_l);
1091 acc += iv.v_u.dot(&*s_s_u);
1092 acc / Number::from(n)
1093 }
1094
1095 pub fn curr_complementarity_min(&self) -> Number {
1102 let cxl = self.curr_compl_x_l();
1103 let cxu = self.curr_compl_x_u();
1104 let csl = self.curr_compl_s_l();
1105 let csu = self.curr_compl_s_u();
1106 let m = |v: &Rc<dyn Vector>| {
1107 if v.dim() == 0 {
1108 Number::INFINITY
1109 } else {
1110 v.min()
1111 }
1112 };
1113 let acc = m(&cxl).min(m(&cxu)).min(m(&csl)).min(m(&csu));
1114 if acc.is_infinite() {
1115 0.0
1116 } else {
1117 acc
1118 }
1119 }
1120
1121 pub fn curr_complementarity_max(&self) -> Number {
1129 self.curr_compl_x_l()
1130 .amax()
1131 .max(self.curr_compl_x_u().amax())
1132 .max(self.curr_compl_s_l().amax())
1133 .max(self.curr_compl_s_u().amax())
1134 }
1135
1136 pub fn curr_centrality_measure(&self) -> Number {
1142 let avrg = self.curr_avrg_compl();
1143 if avrg <= 0.0 {
1144 return 1.0;
1145 }
1146 self.curr_complementarity_min() / avrg
1147 }
1148
1149 pub fn curr_barrier_error(&self) -> Number {
1157 let iv = self.curr_iv();
1158 let (s_d, s_c) = self.optimality_error_scaling(&iv);
1159
1160 let glx = self.curr_grad_lag_x();
1161 let gls = self.curr_grad_lag_s();
1162 let dual = glx.amax().max(gls.amax()) / s_d;
1163
1164 let c = self.curr_c();
1165 let dms = self.curr_d_minus_s();
1166 let primal = c.amax().max(dms.amax());
1167
1168 let compl = self
1169 .curr_relaxed_compl_x_l()
1170 .amax()
1171 .max(self.curr_relaxed_compl_x_u().amax())
1172 .max(self.curr_relaxed_compl_s_l().amax())
1173 .max(self.curr_relaxed_compl_s_u().amax())
1174 / s_c;
1175
1176 dual.max(primal).max(compl)
1177 }
1178
1179 pub fn curr_nlp_error(&self) -> Number {
1193 let iv = self.curr_iv();
1194 let (s_d, s_c) = self.optimality_error_scaling(&iv);
1195
1196 let glx = self.curr_grad_lag_x();
1198 let gls = self.curr_grad_lag_s();
1199 let dual = glx.amax().max(gls.amax()) / s_d;
1200
1201 let c = self.curr_c();
1203 let dms = self.curr_d_minus_s();
1204 let primal = c.amax().max(dms.amax());
1205
1206 let compl = self
1208 .curr_compl_x_l()
1209 .amax()
1210 .max(self.curr_compl_x_u().amax())
1211 .max(self.curr_compl_s_l().amax())
1212 .max(self.curr_compl_s_u().amax())
1213 / s_c;
1214
1215 dual.max(primal).max(compl)
1216 }
1217
1218 fn optimality_error_scaling(&self, iv: &IteratesVector) -> (Number, Number) {
1221 let s_max = self.s_max;
1222
1223 let n_c = iv.z_l.dim() + iv.z_u.dim() + iv.v_l.dim() + iv.v_u.dim();
1226 let s_c = if n_c == 0 {
1227 1.0
1228 } else {
1229 let asum = iv.z_l.asum() + iv.z_u.asum() + iv.v_l.asum() + iv.v_u.asum();
1230 (s_max.max(asum / Number::from(n_c))) / s_max
1231 };
1232
1233 let n_d =
1235 iv.y_c.dim() + iv.y_d.dim() + iv.z_l.dim() + iv.z_u.dim() + iv.v_l.dim() + iv.v_u.dim();
1236 let s_d = if n_d == 0 {
1237 1.0
1238 } else {
1239 let asum = iv.y_c.asum()
1240 + iv.y_d.asum()
1241 + iv.z_l.asum()
1242 + iv.z_u.asum()
1243 + iv.v_l.asum()
1244 + iv.v_u.asum();
1245 (s_max.max(asum / Number::from(n_d))) / s_max
1246 };
1247
1248 (s_d, s_c)
1249 }
1250
1251 pub fn trial_jac_c(&self) -> Rc<dyn Matrix> {
1259 let iv = self.trial_iv();
1260 self.nlp.borrow_mut().eval_jac_c(&*iv.x)
1261 }
1262
1263 pub fn trial_jac_d(&self) -> Rc<dyn Matrix> {
1264 let iv = self.trial_iv();
1265 self.nlp.borrow_mut().eval_jac_d(&*iv.x)
1266 }
1267
1268 pub fn trial_grad_lag_x(&self) -> Rc<dyn Vector> {
1270 let iv = self.trial_iv();
1271 let grad_f = self.trial_grad_f();
1272 let jac_c = self.trial_jac_c();
1273 let jac_d = self.trial_jac_d();
1274
1275 let mut jc_t = iv.x.make_new();
1276 jac_c.trans_mult_vector(1.0, &*iv.y_c, 0.0, &mut *jc_t);
1277 let mut jd_t = iv.x.make_new();
1278 jac_d.trans_mult_vector(1.0, &*iv.y_d, 0.0, &mut *jd_t);
1279
1280 let mut tmp = iv.x.make_new();
1281 tmp.copy(&*grad_f);
1282 tmp.add_two_vectors(1.0, &*jc_t, 1.0, &*jd_t, 1.0);
1283
1284 let nlp = self.nlp.borrow();
1285 nlp.px_l().mult_vector(-1.0, &*iv.z_l, 1.0, &mut *tmp);
1286 nlp.px_u().mult_vector(1.0, &*iv.z_u, 1.0, &mut *tmp);
1287 rc_from(tmp)
1288 }
1289
1290 pub fn trial_grad_lag_s(&self) -> Rc<dyn Vector> {
1292 let iv = self.trial_iv();
1293 let mut tmp = iv.y_d.make_new();
1294 let nlp = self.nlp.borrow();
1295 nlp.pd_u().mult_vector(1.0, &*iv.v_u, 0.0, &mut *tmp);
1296 nlp.pd_l().mult_vector(-1.0, &*iv.v_l, 1.0, &mut *tmp);
1297 tmp.axpy(-1.0, &*iv.y_d);
1298 rc_from(tmp)
1299 }
1300
1301 pub fn trial_compl_x_l(&self) -> Rc<dyn Vector> {
1302 Self::calc_compl(&*self.trial_slack_x_l(), &*self.trial_iv().z_l)
1303 }
1304
1305 pub fn trial_compl_x_u(&self) -> Rc<dyn Vector> {
1306 Self::calc_compl(&*self.trial_slack_x_u(), &*self.trial_iv().z_u)
1307 }
1308
1309 pub fn trial_compl_s_l(&self) -> Rc<dyn Vector> {
1310 Self::calc_compl(&*self.trial_slack_s_l(), &*self.trial_iv().v_l)
1311 }
1312
1313 pub fn trial_compl_s_u(&self) -> Rc<dyn Vector> {
1314 Self::calc_compl(&*self.trial_slack_s_u(), &*self.trial_iv().v_u)
1315 }
1316
1317 fn relaxed_compl_asum(blocks: &[Rc<dyn Vector>], mu: Number) -> Number {
1319 let mut acc = 0.0;
1320 for compl in blocks {
1321 if compl.dim() == 0 {
1322 continue;
1323 }
1324 let mut r = compl.make_new();
1325 r.copy(&**compl);
1326 r.add_scalar(-mu);
1327 acc += r.asum();
1328 }
1329 acc
1330 }
1331
1332 pub fn curr_primal_dual_system_error(&self, mu: Number) -> Number {
1340 let iv = self.curr_iv();
1341 let n_dual = iv.x.dim() + iv.s.dim();
1342 let dual_inf =
1343 (self.curr_grad_lag_x().asum() + self.curr_grad_lag_s().asum()) / Number::from(n_dual);
1344
1345 let n_primal = iv.y_c.dim() + iv.y_d.dim();
1346 let primal_inf = if n_primal > 0 {
1347 (self.curr_c().asum() + self.curr_d_minus_s().asum()) / Number::from(n_primal)
1348 } else {
1349 0.0
1350 };
1351
1352 let n_cmpl = iv.z_l.dim() + iv.z_u.dim() + iv.v_l.dim() + iv.v_u.dim();
1353 let cmpl = if n_cmpl > 0 {
1354 Self::relaxed_compl_asum(
1355 &[
1356 self.curr_compl_x_l(),
1357 self.curr_compl_x_u(),
1358 self.curr_compl_s_l(),
1359 self.curr_compl_s_u(),
1360 ],
1361 mu,
1362 ) / Number::from(n_cmpl)
1363 } else {
1364 0.0
1365 };
1366
1367 dual_inf + primal_inf + cmpl
1368 }
1369
1370 pub fn trial_primal_dual_system_error(&self, mu: Number) -> Number {
1373 let iv = self.trial_iv();
1374 let n_dual = iv.x.dim() + iv.s.dim();
1375 let dual_inf = (self.trial_grad_lag_x().asum() + self.trial_grad_lag_s().asum())
1376 / Number::from(n_dual);
1377
1378 let n_primal = iv.y_c.dim() + iv.y_d.dim();
1379 let primal_inf = if n_primal > 0 {
1380 (self.trial_c().asum() + self.trial_d_minus_s().asum()) / Number::from(n_primal)
1381 } else {
1382 0.0
1383 };
1384
1385 let n_cmpl = iv.z_l.dim() + iv.z_u.dim() + iv.v_l.dim() + iv.v_u.dim();
1386 let cmpl = if n_cmpl > 0 {
1387 Self::relaxed_compl_asum(
1388 &[
1389 self.trial_compl_x_l(),
1390 self.trial_compl_x_u(),
1391 self.trial_compl_s_l(),
1392 self.trial_compl_s_u(),
1393 ],
1394 mu,
1395 ) / Number::from(n_cmpl)
1396 } else {
1397 0.0
1398 };
1399
1400 dual_inf + primal_inf + cmpl
1401 }
1402
1403 fn damping_indicators(&self) -> DampingIndicators {
1414 let nlp = self.nlp.borrow();
1415
1416 let mut tmp_x_l = nlp.x_l().make_new();
1417 tmp_x_l.set(1.0);
1418 let mut tmp_x_u = nlp.x_u().make_new();
1419 tmp_x_u.set(1.0);
1420 let mut tmp_x = self.curr_iv().x.make_new();
1421 nlp.px_l().mult_vector(1.0, &*tmp_x_l, 0.0, &mut *tmp_x);
1422 nlp.px_u().mult_vector(-1.0, &*tmp_x_u, 1.0, &mut *tmp_x);
1423 let mut d_x_l = nlp.x_l().make_new();
1424 nlp.px_l().trans_mult_vector(1.0, &*tmp_x, 0.0, &mut *d_x_l);
1425 let mut d_x_u = nlp.x_u().make_new();
1426 nlp.px_u()
1427 .trans_mult_vector(-1.0, &*tmp_x, 0.0, &mut *d_x_u);
1428
1429 let mut tmp_s_l = nlp.d_l().make_new();
1430 tmp_s_l.set(1.0);
1431 let mut tmp_s_u = nlp.d_u().make_new();
1432 tmp_s_u.set(1.0);
1433 let mut tmp_s = self.curr_iv().s.make_new();
1434 nlp.pd_l().mult_vector(1.0, &*tmp_s_l, 0.0, &mut *tmp_s);
1435 nlp.pd_u().mult_vector(-1.0, &*tmp_s_u, 1.0, &mut *tmp_s);
1436 let mut d_s_l = nlp.d_l().make_new();
1437 nlp.pd_l().trans_mult_vector(1.0, &*tmp_s, 0.0, &mut *d_s_l);
1438 let mut d_s_u = nlp.d_u().make_new();
1439 nlp.pd_u()
1440 .trans_mult_vector(-1.0, &*tmp_s, 0.0, &mut *d_s_u);
1441
1442 DampingIndicators {
1443 x_l: rc_from(d_x_l),
1444 x_u: rc_from(d_x_u),
1445 s_l: rc_from(d_s_l),
1446 s_u: rc_from(d_s_u),
1447 }
1448 }
1449
1450 pub fn curr_grad_lag_with_damping_x(&self) -> Rc<dyn Vector> {
1455 if self.kappa_d == 0.0 {
1456 return self.curr_grad_lag_x();
1457 }
1458 let mu = self.data.borrow().curr_mu;
1459 let di = self.damping_indicators();
1460 let (d_x_l, d_x_u) = (di.x_l, di.x_u);
1461 let glx = self.curr_grad_lag_x();
1462 let mut tmp = glx.make_new();
1463 tmp.copy(&*glx);
1464 let nlp = self.nlp.borrow();
1465 nlp.px_l()
1466 .mult_vector(self.kappa_d * mu, &*d_x_l, 1.0, &mut *tmp);
1467 nlp.px_u()
1468 .mult_vector(-self.kappa_d * mu, &*d_x_u, 1.0, &mut *tmp);
1469 rc_from(tmp)
1470 }
1471
1472 pub fn curr_grad_lag_with_damping_s(&self) -> Rc<dyn Vector> {
1473 if self.kappa_d == 0.0 {
1474 return self.curr_grad_lag_s();
1475 }
1476 let mu = self.data.borrow().curr_mu;
1477 let di = self.damping_indicators();
1478 let (d_s_l, d_s_u) = (di.s_l, di.s_u);
1479 let gls = self.curr_grad_lag_s();
1480 let mut tmp = gls.make_new();
1481 tmp.copy(&*gls);
1482 let nlp = self.nlp.borrow();
1483 nlp.pd_l()
1484 .mult_vector(self.kappa_d * mu, &*d_s_l, 1.0, &mut *tmp);
1485 nlp.pd_u()
1486 .mult_vector(-self.kappa_d * mu, &*d_s_u, 1.0, &mut *tmp);
1487 rc_from(tmp)
1488 }
1489
1490 pub fn grad_kappa_times_damping_x(&self) -> Rc<dyn Vector> {
1497 let mut tmp = self.curr_iv().x.make_new();
1498 tmp.set(0.0);
1499 if self.kappa_d > 0.0 {
1500 let di = self.damping_indicators();
1501 let nlp = self.nlp.borrow();
1502 nlp.px_l()
1503 .mult_vector(self.kappa_d, &*di.x_l, 0.0, &mut *tmp);
1504 nlp.px_u()
1505 .mult_vector(-self.kappa_d, &*di.x_u, 1.0, &mut *tmp);
1506 }
1507 rc_from(tmp)
1508 }
1509
1510 pub fn grad_kappa_times_damping_s(&self) -> Rc<dyn Vector> {
1511 let mut tmp = self.curr_iv().s.make_new();
1512 tmp.set(0.0);
1513 if self.kappa_d > 0.0 {
1514 let di = self.damping_indicators();
1515 let nlp = self.nlp.borrow();
1516 nlp.pd_l()
1517 .mult_vector(self.kappa_d, &*di.s_l, 0.0, &mut *tmp);
1518 nlp.pd_u()
1519 .mult_vector(-self.kappa_d, &*di.s_u, 1.0, &mut *tmp);
1520 }
1521 rc_from(tmp)
1522 }
1523
1524 pub fn aff_step_alpha_primal_max(&self, delta_aff: &IteratesVector, tau: Number) -> Number {
1537 let nlp = self.nlp.borrow();
1538 let s_x_l = self.curr_slack_x_l();
1539 let s_x_u = self.curr_slack_x_u();
1540 let s_s_l = self.curr_slack_s_l();
1541 let s_s_u = self.curr_slack_s_u();
1542
1543 let mut step_x_l = s_x_l.make_new();
1545 nlp.px_l()
1546 .trans_mult_vector(1.0, &*delta_aff.x, 0.0, &mut *step_x_l);
1547 let mut step_x_u = s_x_u.make_new();
1548 nlp.px_u()
1549 .trans_mult_vector(-1.0, &*delta_aff.x, 0.0, &mut *step_x_u);
1550 let mut step_s_l = s_s_l.make_new();
1551 nlp.pd_l()
1552 .trans_mult_vector(1.0, &*delta_aff.s, 0.0, &mut *step_s_l);
1553 let mut step_s_u = s_s_u.make_new();
1554 nlp.pd_u()
1555 .trans_mult_vector(-1.0, &*delta_aff.s, 0.0, &mut *step_s_u);
1556
1557 s_x_l
1558 .frac_to_bound(&*step_x_l, tau)
1559 .min(s_x_u.frac_to_bound(&*step_x_u, tau))
1560 .min(s_s_l.frac_to_bound(&*step_s_l, tau))
1561 .min(s_s_u.frac_to_bound(&*step_s_u, tau))
1562 }
1563
1564 pub fn aff_step_alpha_dual_max(&self, delta_aff: &IteratesVector, tau: Number) -> Number {
1566 let iv = self.curr_iv();
1567 iv.z_l
1568 .frac_to_bound(&*delta_aff.z_l, tau)
1569 .min(iv.z_u.frac_to_bound(&*delta_aff.z_u, tau))
1570 .min(iv.v_l.frac_to_bound(&*delta_aff.v_l, tau))
1571 .min(iv.v_u.frac_to_bound(&*delta_aff.v_u, tau))
1572 }
1573
1574 pub fn aff_step_compl_avrg(
1578 &self,
1579 delta_aff: &IteratesVector,
1580 alpha_primal: Number,
1581 alpha_dual: Number,
1582 ) -> Number {
1583 let iv = self.curr_iv();
1584 let n = iv.z_l.dim() + iv.z_u.dim() + iv.v_l.dim() + iv.v_u.dim();
1585 if n == 0 {
1586 return 0.0;
1587 }
1588 let nlp = self.nlp.borrow();
1589
1590 let s_x_l = self.curr_slack_x_l();
1592 let mut s_x_l_aff = s_x_l.make_new();
1593 s_x_l_aff.copy(&*s_x_l);
1594 let mut tmp = s_x_l.make_new();
1595 nlp.px_l()
1596 .trans_mult_vector(1.0, &*delta_aff.x, 0.0, &mut *tmp);
1597 s_x_l_aff.axpy(alpha_primal, &*tmp);
1598 let mut z_l_aff = iv.z_l.make_new();
1600 z_l_aff.copy(&*iv.z_l);
1601 z_l_aff.axpy(alpha_dual, &*delta_aff.z_l);
1602 let mut acc = s_x_l_aff.dot(&*z_l_aff);
1603
1604 let s_x_u = self.curr_slack_x_u();
1606 let mut s_x_u_aff = s_x_u.make_new();
1607 s_x_u_aff.copy(&*s_x_u);
1608 let mut tmp = s_x_u.make_new();
1609 nlp.px_u()
1610 .trans_mult_vector(-1.0, &*delta_aff.x, 0.0, &mut *tmp);
1611 s_x_u_aff.axpy(alpha_primal, &*tmp);
1612 let mut z_u_aff = iv.z_u.make_new();
1613 z_u_aff.copy(&*iv.z_u);
1614 z_u_aff.axpy(alpha_dual, &*delta_aff.z_u);
1615 acc += s_x_u_aff.dot(&*z_u_aff);
1616
1617 let s_s_l = self.curr_slack_s_l();
1619 let mut s_s_l_aff = s_s_l.make_new();
1620 s_s_l_aff.copy(&*s_s_l);
1621 let mut tmp = s_s_l.make_new();
1622 nlp.pd_l()
1623 .trans_mult_vector(1.0, &*delta_aff.s, 0.0, &mut *tmp);
1624 s_s_l_aff.axpy(alpha_primal, &*tmp);
1625 let mut v_l_aff = iv.v_l.make_new();
1626 v_l_aff.copy(&*iv.v_l);
1627 v_l_aff.axpy(alpha_dual, &*delta_aff.v_l);
1628 acc += s_s_l_aff.dot(&*v_l_aff);
1629
1630 let s_s_u = self.curr_slack_s_u();
1632 let mut s_s_u_aff = s_s_u.make_new();
1633 s_s_u_aff.copy(&*s_s_u);
1634 let mut tmp = s_s_u.make_new();
1635 nlp.pd_u()
1636 .trans_mult_vector(-1.0, &*delta_aff.s, 0.0, &mut *tmp);
1637 s_s_u_aff.axpy(alpha_primal, &*tmp);
1638 let mut v_u_aff = iv.v_u.make_new();
1639 v_u_aff.copy(&*iv.v_u);
1640 v_u_aff.axpy(alpha_dual, &*delta_aff.v_u);
1641 acc += s_s_u_aff.dot(&*v_u_aff);
1642
1643 acc / Number::from(n)
1644 }
1645}
1646
1647pub type IpoptCqHandle = Rc<RefCell<IpoptCalculatedQuantities>>;
1649
1650struct DampingIndicators {
1654 x_l: Rc<dyn Vector>,
1655 x_u: Rc<dyn Vector>,
1656 s_l: Rc<dyn Vector>,
1657 s_u: Rc<dyn Vector>,
1658}
1659
1660#[cfg(test)]
1661mod tests {
1662 use super::*;
1663 use crate::ipopt_data::IpoptData;
1664 use crate::iterates_vector::IteratesVector;
1665 use pounce_common::types::Index;
1666 use pounce_linalg::dense_vector::{DenseVector, DenseVectorSpace};
1667 use pounce_linalg::expansion_matrix::{ExpansionMatrix, ExpansionMatrixSpace};
1668 use pounce_linalg::triplet::{GenTMatrix, GenTMatrixSpace};
1669 use std::rc::Rc as StdRc;
1670
1671 fn dvec(values: &[Number]) -> DenseVector {
1672 let space = DenseVectorSpace::new(values.len() as Index);
1673 let mut v = space.make_new_dense();
1674 v.values_mut().copy_from_slice(values);
1675 v
1676 }
1677
1678 fn rcv(values: &[Number]) -> Rc<dyn Vector> {
1679 StdRc::new(dvec(values))
1680 }
1681
1682 struct MockNlp {
1688 x_l: DenseVector,
1689 x_u: DenseVector,
1690 d_l: DenseVector,
1691 d_u: DenseVector,
1692 px_l: Rc<dyn Matrix>,
1693 px_u: Rc<dyn Matrix>,
1694 pd_l: Rc<dyn Matrix>,
1695 pd_u: Rc<dyn Matrix>,
1696 obj_scale: Number,
1701 c_scale: Option<Vec<Number>>,
1702 d_scale: Option<Vec<Number>>,
1703 }
1704
1705 impl MockNlp {
1706 fn with_scaling(
1707 mut self,
1708 obj_scale: Number,
1709 c_scale: Option<Vec<Number>>,
1710 d_scale: Option<Vec<Number>>,
1711 ) -> Self {
1712 self.obj_scale = obj_scale;
1713 self.c_scale = c_scale;
1714 self.d_scale = d_scale;
1715 self
1716 }
1717
1718 fn new() -> Self {
1719 let x_l = dvec(&[0.0]);
1721 let x_u = dvec(&[5.0]);
1723 let d_l = dvec(&[1.0]);
1725 let d_u = dvec(&[]);
1726
1727 let px_l_space = ExpansionMatrixSpace::new(2, 1, &[0], 0);
1728 let px_u_space = ExpansionMatrixSpace::new(2, 1, &[1], 0);
1729 let pd_l_space = ExpansionMatrixSpace::new(1, 1, &[0], 0);
1730 let pd_u_space = ExpansionMatrixSpace::new(1, 0, &[], 0);
1731
1732 Self {
1733 x_l,
1734 x_u,
1735 d_l,
1736 d_u,
1737 px_l: StdRc::new(ExpansionMatrix::new(px_l_space)),
1738 px_u: StdRc::new(ExpansionMatrix::new(px_u_space)),
1739 pd_l: StdRc::new(ExpansionMatrix::new(pd_l_space)),
1740 pd_u: StdRc::new(ExpansionMatrix::new(pd_u_space)),
1741 obj_scale: 1.0,
1742 c_scale: None,
1743 d_scale: None,
1744 }
1745 }
1746 }
1747
1748 impl crate::ipopt_nlp::Nlp for MockNlp {
1749 fn n(&self) -> Index {
1750 2
1751 }
1752 fn m_eq(&self) -> Index {
1753 1
1754 }
1755 fn m_ineq(&self) -> Index {
1756 1
1757 }
1758 fn eval_f(&mut self, x: &dyn Vector) -> Number {
1759 let xx = x.as_any().downcast_ref::<DenseVector>().unwrap();
1761 xx.values()[0] * xx.values()[0] + xx.values()[1] * xx.values()[1]
1762 }
1763 fn eval_grad_f(&mut self, x: &dyn Vector, g: &mut dyn Vector) {
1764 let xx = x.as_any().downcast_ref::<DenseVector>().unwrap();
1766 let gg = g.as_any_mut().downcast_mut::<DenseVector>().unwrap();
1767 gg.values_mut()[0] = 2.0 * xx.values()[0];
1768 gg.values_mut()[1] = 2.0 * xx.values()[1];
1769 }
1770 fn eval_c(&mut self, x: &dyn Vector, c: &mut dyn Vector) {
1771 let xx = x.as_any().downcast_ref::<DenseVector>().unwrap();
1772 let cc = c.as_any_mut().downcast_mut::<DenseVector>().unwrap();
1773 cc.values_mut()[0] = xx.values()[0] + xx.values()[1] - 1.0;
1774 }
1775 fn eval_d(&mut self, x: &dyn Vector, d: &mut dyn Vector) {
1776 let xx = x.as_any().downcast_ref::<DenseVector>().unwrap();
1777 let dd = d.as_any_mut().downcast_mut::<DenseVector>().unwrap();
1778 dd.values_mut()[0] = xx.values()[0];
1779 }
1780 fn eval_jac_c(&mut self, _x: &dyn Vector) -> Rc<dyn Matrix> {
1781 let space = GenTMatrixSpace::new(1, 2, vec![1, 1], vec![1, 2]);
1783 let mut jac = GenTMatrix::new(space);
1784 jac.set_values(&[1.0, 1.0]);
1785 StdRc::new(jac)
1786 }
1787 fn eval_jac_d(&mut self, _x: &dyn Vector) -> Rc<dyn Matrix> {
1788 let space = GenTMatrixSpace::new(1, 2, vec![1], vec![1]);
1790 let mut jac = GenTMatrix::new(space);
1791 jac.set_values(&[1.0]);
1792 StdRc::new(jac)
1793 }
1794 fn eval_h(
1795 &mut self,
1796 _x: &dyn Vector,
1797 _obj_factor: Number,
1798 _y_c: &dyn Vector,
1799 _y_d: &dyn Vector,
1800 ) -> Rc<dyn SymMatrix> {
1801 unimplemented!()
1802 }
1803 }
1804
1805 impl IpoptNlp for MockNlp {
1806 fn x_l(&self) -> &dyn Vector {
1807 &self.x_l
1808 }
1809 fn x_u(&self) -> &dyn Vector {
1810 &self.x_u
1811 }
1812 fn d_l(&self) -> &dyn Vector {
1813 &self.d_l
1814 }
1815 fn d_u(&self) -> &dyn Vector {
1816 &self.d_u
1817 }
1818 fn px_l(&self) -> Rc<dyn Matrix> {
1819 self.px_l.clone()
1820 }
1821 fn px_u(&self) -> Rc<dyn Matrix> {
1822 self.px_u.clone()
1823 }
1824 fn pd_l(&self) -> Rc<dyn Matrix> {
1825 self.pd_l.clone()
1826 }
1827 fn pd_u(&self) -> Rc<dyn Matrix> {
1828 self.pd_u.clone()
1829 }
1830 fn obj_scaling_factor(&self) -> Number {
1831 self.obj_scale
1832 }
1833 fn c_scale_vec(&self) -> Option<Vec<Number>> {
1834 self.c_scale.clone()
1835 }
1836 fn d_scale_vec(&self) -> Option<Vec<Number>> {
1837 self.d_scale.clone()
1838 }
1839 }
1840
1841 fn fixture() -> IpoptCalculatedQuantities {
1842 fixture_with(MockNlp::new())
1843 }
1844
1845 fn fixture_with(nlp: MockNlp) -> IpoptCalculatedQuantities {
1846 let mut data = IpoptData::new();
1847 data.curr_mu = 0.1;
1848 let iv = IteratesVector::new(
1852 rcv(&[2.0, 3.0]),
1853 rcv(&[4.0]),
1854 rcv(&[1.0]),
1855 rcv(&[1.0]),
1856 rcv(&[0.5]),
1857 rcv(&[0.7]),
1858 rcv(&[0.3]),
1859 rcv(&[]),
1860 );
1861 data.set_curr(iv);
1862 let data_handle = StdRc::new(RefCell::new(data));
1863 let nlp: StdRc<RefCell<dyn IpoptNlp>> = StdRc::new(RefCell::new(nlp));
1864 let mut cq = IpoptCalculatedQuantities::new(data_handle, nlp);
1865 cq.kappa_d = 0.0;
1867 cq
1868 }
1869
1870 fn dense_vals(v: &Rc<dyn Vector>) -> Vec<Number> {
1871 v.as_any()
1872 .downcast_ref::<DenseVector>()
1873 .unwrap()
1874 .values()
1875 .to_vec()
1876 }
1877
1878 #[test]
1879 fn slack_x_lower_is_x0_minus_x_l() {
1880 let cq = fixture();
1882 assert_eq!(dense_vals(&cq.curr_slack_x_l()), vec![2.0]);
1883 }
1884
1885 #[test]
1886 fn slack_x_upper_is_x_u_minus_x1() {
1887 let cq = fixture();
1889 assert_eq!(dense_vals(&cq.curr_slack_x_u()), vec![2.0]);
1890 }
1891
1892 #[test]
1893 fn slack_s_lower() {
1894 let cq = fixture();
1896 assert_eq!(dense_vals(&cq.curr_slack_s_l()), vec![3.0]);
1897 }
1898
1899 #[test]
1900 fn grad_f_is_twice_x() {
1901 let cq = fixture();
1902 assert_eq!(dense_vals(&cq.curr_grad_f()), vec![4.0, 6.0]);
1903 }
1904
1905 #[test]
1906 fn compl_x_l_is_slack_times_z() {
1907 let cq = fixture();
1909 assert_eq!(dense_vals(&cq.curr_compl_x_l()), vec![1.0]);
1910 }
1911
1912 #[test]
1913 fn relaxed_compl_x_l_subtracts_mu() {
1914 let cq = fixture();
1916 assert!((dense_vals(&cq.curr_relaxed_compl_x_l())[0] - 0.9).abs() < 1e-15);
1917 }
1918
1919 #[test]
1920 fn sigma_x_routes_z_over_slack_through_p() {
1921 let cq = fixture();
1925 let s = dense_vals(&cq.curr_sigma_x());
1926 assert!((s[0] - 0.25).abs() < 1e-15);
1927 assert!((s[1] - 0.35).abs() < 1e-15);
1928 }
1929
1930 #[test]
1931 fn sigma_s_lower_only() {
1932 let cq = fixture();
1934 let s = dense_vals(&cq.curr_sigma_s());
1935 assert!((s[0] - 0.1).abs() < 1e-15);
1936 }
1937
1938 #[test]
1939 fn avrg_compl_averages_over_active_bounds() {
1940 let cq = fixture();
1945 assert!((cq.curr_avrg_compl() - 1.1).abs() < 1e-15);
1946 }
1947
1948 #[test]
1949 fn complementarity_min_takes_min_over_active_pairs() {
1950 let cq = fixture();
1953 assert!((cq.curr_complementarity_min() - 0.9).abs() < 1e-15);
1954 }
1955
1956 #[test]
1957 fn centrality_measure_is_min_over_avrg() {
1958 let cq = fixture();
1960 let xi = cq.curr_centrality_measure();
1961 assert!((xi - 0.9 / 1.1).abs() < 1e-15);
1962 }
1963
1964 #[test]
1965 fn curr_f_evaluates_objective() {
1966 let cq = fixture();
1968 assert!((cq.curr_f() - 13.0).abs() < 1e-15);
1969 }
1970
1971 #[test]
1972 fn curr_barrier_obj_subtracts_mu_log_slacks() {
1973 let cq = fixture();
1977 let expected = 13.0 - 0.1 * (2.0 * 2.0_f64.ln() + 3.0_f64.ln());
1978 assert!((cq.curr_barrier_obj() - expected).abs() < 1e-13);
1979 }
1980
1981 #[test]
1982 fn curr_constraint_violation_is_one_norm() {
1983 let cq = fixture();
1987 assert!((cq.curr_constraint_violation() - 6.0).abs() < 1e-13);
1988 }
1989
1990 #[test]
1991 fn grad_barrier_obj_x_subtracts_mu_inv_slack() {
1992 let cq = fixture();
1997 let g = dense_vals(&cq.curr_grad_barrier_obj_x());
1998 assert!((g[0] - 3.95).abs() < 1e-13);
1999 assert!((g[1] - 6.05).abs() < 1e-13);
2000 }
2001
2002 #[test]
2003 fn grad_lag_s_is_minus_y_d_minus_pl_v_l_plus_pu_v_u() {
2004 let cq = fixture();
2008 assert!((dense_vals(&cq.curr_grad_lag_s())[0] + 1.3).abs() < 1e-15);
2009 }
2010
2011 fn zero_iv_like(iv: &IteratesVector) -> IteratesVector {
2012 IteratesVector::new(
2015 rcv(&vec![0.0; iv.x.dim() as usize]),
2016 rcv(&vec![0.0; iv.s.dim() as usize]),
2017 rcv(&vec![0.0; iv.y_c.dim() as usize]),
2018 rcv(&vec![0.0; iv.y_d.dim() as usize]),
2019 rcv(&vec![0.0; iv.z_l.dim() as usize]),
2020 rcv(&vec![0.0; iv.z_u.dim() as usize]),
2021 rcv(&vec![0.0; iv.v_l.dim() as usize]),
2022 rcv(&vec![0.0; iv.v_u.dim() as usize]),
2023 )
2024 }
2025
2026 #[test]
2027 fn aff_step_compl_avrg_with_zero_step_matches_curr_avrg_compl() {
2028 let cq = fixture();
2032 let iv = cq.curr_iv();
2033 let zero = zero_iv_like(&iv);
2034 let m = cq.aff_step_compl_avrg(&zero, 1.0, 1.0);
2035 assert!((m - 1.1).abs() < 1e-13);
2036 assert!((cq.curr_avrg_compl() - 1.1).abs() < 1e-13);
2037 }
2038
2039 #[test]
2040 fn aff_step_compl_avrg_responds_to_primal_step() {
2041 let cq = fixture();
2045 let iv = cq.curr_iv();
2046 let mut z = zero_iv_like(&iv);
2047 z.x = rcv(&[1.0, 0.0]);
2048 let m = cq.aff_step_compl_avrg(&z, 1.0, 1.0);
2049 assert!((m - 1.2666666666666666).abs() < 1e-13);
2050 }
2051
2052 #[test]
2053 fn aff_step_alpha_primal_truncates_to_x_lower_bound() {
2054 let cq = fixture();
2056 let iv = cq.curr_iv();
2057 let mut z = zero_iv_like(&iv);
2058 z.x = rcv(&[-3.0, 0.0]);
2059 let a = cq.aff_step_alpha_primal_max(&z, 1.0);
2060 assert!((a - 2.0 / 3.0).abs() < 1e-13);
2061 }
2062
2063 #[test]
2064 fn aff_step_alpha_dual_truncates_to_z_lower_bound() {
2065 let cq = fixture();
2067 let iv = cq.curr_iv();
2068 let mut z = zero_iv_like(&iv);
2069 z.z_l = rcv(&[-1.0]);
2070 let a = cq.aff_step_alpha_dual_max(&z, 1.0);
2071 assert!((a - 0.5).abs() < 1e-13);
2072 }
2073
2074 #[test]
2075 fn grad_barr_t_delta_dots_barrier_grads_with_step() {
2076 let cq = fixture();
2080 let dx = dvec(&[1.0, 2.0]);
2081 let ds = dvec(&[3.0]);
2082 let r = cq.curr_grad_barr_t_delta(&dx, &ds);
2083 let expected = 3.95 + 12.10 - 0.1;
2084 assert!((r - expected).abs() < 1e-13, "r = {r}");
2085 }
2086
2087 #[test]
2088 fn dwd_with_no_w_collapses_to_sigma_quadratic() {
2089 let cq = fixture();
2094 let dx = dvec(&[2.0, -1.0]);
2095 let ds = dvec(&[3.0]);
2096 let r = cq.curr_dwd(&dx, &ds);
2097 assert!((r - 2.25).abs() < 1e-13, "r = {r}");
2098 }
2099
2100 #[test]
2101 fn dwd_includes_pd_perturbations() {
2102 let cq = fixture();
2108 {
2109 let mut d = cq.data.borrow_mut();
2110 d.perturbations.delta_x = 0.5;
2111 d.perturbations.delta_s = 0.25;
2112 }
2113 let dx = dvec(&[2.0, -1.0]);
2114 let ds = dvec(&[3.0]);
2115 let r = cq.curr_dwd(&dx, &ds);
2116 assert!((r - 7.00).abs() < 1e-13, "r = {r}");
2117 }
2118
2119 #[test]
2122 fn unscaled_dual_inf_is_scaled_over_df() {
2123 let cq = fixture_with(MockNlp::new().with_scaling(2.0, None, None));
2127 let scaled = cq.curr_dual_infeasibility_max();
2128 let unscaled = cq.curr_unscaled_dual_infeasibility_max();
2129 assert!(scaled > 0.0, "fixture should have nonzero dual inf");
2130 assert!(
2131 (unscaled - scaled / 2.0).abs() < 1e-12,
2132 "unscaled {unscaled} != scaled/df {}",
2133 scaled / 2.0
2134 );
2135 }
2136
2137 #[test]
2138 fn unscaled_residuals_are_identity_without_scaling() {
2139 let cq = fixture();
2142 assert_eq!(
2143 cq.curr_unscaled_dual_infeasibility_max(),
2144 cq.curr_dual_infeasibility_max()
2145 );
2146 assert_eq!(
2147 cq.curr_unscaled_complementarity_max(),
2148 cq.curr_complementarity_max()
2149 );
2150 assert_eq!(
2151 cq.curr_unscaled_primal_infeasibility_max(),
2152 cq.curr_primal_infeasibility_max()
2153 );
2154 }
2155
2156 #[test]
2157 fn unscaled_compl_is_scaled_over_df() {
2158 let cq = fixture_with(MockNlp::new().with_scaling(2.0, None, None));
2159 let scaled = cq.curr_complementarity_max();
2160 let unscaled = cq.curr_unscaled_complementarity_max();
2161 assert!(scaled > 0.0);
2162 assert!((unscaled - scaled / 2.0).abs() < 1e-12);
2163 }
2164
2165 #[test]
2166 fn unscaled_primal_divides_each_row_by_its_factor() {
2167 let cq = fixture_with(MockNlp::new().with_scaling(1.0, Some(vec![4.0]), Some(vec![2.0])));
2171 assert!((cq.curr_primal_infeasibility_max() - 4.0).abs() < 1e-12);
2172 assert!(
2173 (cq.curr_unscaled_primal_infeasibility_max() - 1.0).abs() < 1e-12,
2174 "got {}",
2175 cq.curr_unscaled_primal_infeasibility_max()
2176 );
2177 }
2178
2179 #[test]
2180 fn unscaled_nlp_error_is_max_of_unscaled_components() {
2181 let cq = fixture_with(MockNlp::new().with_scaling(2.0, Some(vec![4.0]), Some(vec![2.0])));
2182 let expected = cq
2183 .curr_unscaled_dual_infeasibility_max()
2184 .max(cq.curr_unscaled_primal_infeasibility_max())
2185 .max(cq.curr_unscaled_complementarity_max());
2186 assert_eq!(cq.curr_unscaled_nlp_error(), expected);
2187 }
2188}