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 pub slack_move: Number,
47
48 curr_slack_x_l_cache: RefCell<Cache<Rc<dyn Vector>>>,
57 curr_slack_x_u_cache: RefCell<Cache<Rc<dyn Vector>>>,
58 curr_slack_s_l_cache: RefCell<Cache<Rc<dyn Vector>>>,
59 curr_slack_s_u_cache: RefCell<Cache<Rc<dyn Vector>>>,
60 curr_sigma_x_cache: RefCell<Cache<Rc<dyn Vector>>>,
61 curr_sigma_s_cache: RefCell<Cache<Rc<dyn Vector>>>,
62}
63
64fn rc_from(v: Box<dyn Vector>) -> Rc<dyn Vector> {
67 Rc::from(v)
68}
69
70pub struct AdjustedBounds {
74 pub adjusted: usize,
76 pub x_l: Box<dyn Vector>,
77 pub x_u: Box<dyn Vector>,
78 pub d_l: Box<dyn Vector>,
79 pub d_u: Box<dyn Vector>,
80}
81
82impl IpoptCalculatedQuantities {
83 pub fn new(data: IpoptDataHandle, nlp: Rc<RefCell<dyn IpoptNlp>>) -> Self {
84 Self {
85 data,
86 nlp,
87 s_max: 100.0,
88 kappa_d: 1e-5,
89 slack_move: f64::EPSILON.powf(0.75),
90 curr_slack_x_l_cache: RefCell::new(Cache::new(1)),
91 curr_slack_x_u_cache: RefCell::new(Cache::new(1)),
92 curr_slack_s_l_cache: RefCell::new(Cache::new(1)),
93 curr_slack_s_u_cache: RefCell::new(Cache::new(1)),
94 curr_sigma_x_cache: RefCell::new(Cache::new(1)),
95 curr_sigma_s_cache: RefCell::new(Cache::new(1)),
96 }
97 }
98
99 pub fn data(&self) -> &IpoptDataHandle {
100 &self.data
101 }
102
103 pub fn nlp(&self) -> &Rc<RefCell<dyn IpoptNlp>> {
104 &self.nlp
105 }
106
107 pub(crate) fn curr_iv(&self) -> IteratesVector {
108 let Some(iv) = self.data.borrow().curr.as_ref().cloned() else {
109 unreachable!("IpoptCalculatedQuantities: curr iterate not set");
110 };
111 iv
112 }
113
114 fn trial_iv(&self) -> IteratesVector {
115 let Some(iv) = self.data.borrow().trial.as_ref().cloned() else {
116 unreachable!("IpoptCalculatedQuantities: trial iterate not set");
117 };
118 iv
119 }
120
121 fn calc_slack_l_box(p: &dyn Matrix, x: &dyn Vector, x_bound: &dyn Vector) -> Box<dyn Vector> {
128 let mut result = x_bound.make_new();
129 result.copy(x_bound);
130 p.trans_mult_vector(1.0, x, -1.0, &mut *result);
132 result
133 }
134
135 fn calc_slack_u_box(p: &dyn Matrix, x: &dyn Vector, x_bound: &dyn Vector) -> Box<dyn Vector> {
136 let mut result = x_bound.make_new();
137 result.copy(x_bound);
138 p.trans_mult_vector(-1.0, x, 1.0, &mut *result);
140 result
141 }
142
143 fn calculate_safe_slack(
152 &self,
153 slack: &mut dyn Vector,
154 bound: &dyn Vector,
155 multiplier: &dyn Vector,
156 mu: Number,
157 ) -> usize {
158 if slack.dim() == 0 {
159 return 0;
160 }
161 let min_slack = slack.min();
162 let mut s_min = f64::EPSILON * mu.min(1.0);
166 if s_min == 0.0 {
167 s_min = f64::MIN_POSITIVE;
168 }
169 if min_slack >= s_min {
170 return 0;
171 }
172
173 let mut t = slack.make_new();
176 t.copy(&*slack);
177 t.add_scalar(-s_min);
178 t.element_wise_sgn();
179 let mut zero_vec = t.make_new();
180 zero_vec.set(0.0);
181 t.element_wise_min(&*zero_vec); t.scal(-1.0); let retval = t.asum().round() as usize;
184
185 slack.element_wise_max(&*zero_vec);
188
189 let mut t2 = t.make_new();
191 let mut s_min_vec = t2.make_new();
192 s_min_vec.set(s_min);
193 if mu != 0.0 {
194 t2.set(mu);
196 t2.element_wise_divide(multiplier);
197 t2.element_wise_max(&*s_min_vec);
198 } else {
199 t2.copy(&*s_min_vec);
203 }
204 t2.axpy(-1.0, &*slack);
205
206 t.element_wise_select(&*t2);
208 t.axpy(1.0, &*slack);
209
210 let mut t_max = t2; t_max.set(1.0);
213 let mut abs_bound = bound.make_new();
214 abs_bound.copy(bound);
215 abs_bound.element_wise_abs();
216 t_max.element_wise_max(&*abs_bound);
217 t_max.add_one_vector(1.0, &*slack, self.slack_move);
219
220 t.element_wise_min(&*t_max);
222 slack.copy(&*t);
223 retval
224 }
225
226 fn safe_slack_l(
230 &self,
231 p: &dyn Matrix,
232 x: &dyn Vector,
233 bound: &dyn Vector,
234 multiplier: &dyn Vector,
235 ) -> (Rc<dyn Vector>, usize) {
236 let mu = self.data.borrow().curr_mu;
237 let mut result = Self::calc_slack_l_box(p, x, bound);
238 let n = self.calculate_safe_slack(&mut *result, bound, multiplier, mu);
239 (rc_from(result), n)
240 }
241
242 fn safe_slack_u(
243 &self,
244 p: &dyn Matrix,
245 x: &dyn Vector,
246 bound: &dyn Vector,
247 multiplier: &dyn Vector,
248 ) -> (Rc<dyn Vector>, usize) {
249 let mu = self.data.borrow().curr_mu;
250 let mut result = Self::calc_slack_u_box(p, x, bound);
251 let n = self.calculate_safe_slack(&mut *result, bound, multiplier, mu);
252 (rc_from(result), n)
253 }
254
255 pub fn curr_slack_x_l(&self) -> Rc<dyn Vector> {
256 let iv = self.curr_iv();
257 {
258 let cache = self.curr_slack_x_l_cache.borrow();
259 if let Some(v) = cache.get(&[iv.x.as_tagged()], &[]) {
260 return v;
261 }
262 }
263 let nlp = self.nlp.borrow();
264 let (v, _) = self.safe_slack_l(&*nlp.px_l(), &*iv.x, nlp.x_l(), &*iv.z_l);
265 self.curr_slack_x_l_cache
266 .borrow_mut()
267 .add(v.clone(), &[iv.x.as_tagged()], &[]);
268 v
269 }
270
271 pub fn curr_slack_x_u(&self) -> Rc<dyn Vector> {
272 let iv = self.curr_iv();
273 {
274 let cache = self.curr_slack_x_u_cache.borrow();
275 if let Some(v) = cache.get(&[iv.x.as_tagged()], &[]) {
276 return v;
277 }
278 }
279 let nlp = self.nlp.borrow();
280 let (v, _) = self.safe_slack_u(&*nlp.px_u(), &*iv.x, nlp.x_u(), &*iv.z_u);
281 self.curr_slack_x_u_cache
282 .borrow_mut()
283 .add(v.clone(), &[iv.x.as_tagged()], &[]);
284 v
285 }
286
287 pub fn curr_slack_s_l(&self) -> Rc<dyn Vector> {
288 let iv = self.curr_iv();
289 {
290 let cache = self.curr_slack_s_l_cache.borrow();
291 if let Some(v) = cache.get(&[iv.s.as_tagged()], &[]) {
292 return v;
293 }
294 }
295 let nlp = self.nlp.borrow();
296 let (v, _) = self.safe_slack_l(&*nlp.pd_l(), &*iv.s, nlp.d_l(), &*iv.v_l);
297 self.curr_slack_s_l_cache
298 .borrow_mut()
299 .add(v.clone(), &[iv.s.as_tagged()], &[]);
300 v
301 }
302
303 pub fn curr_slack_s_u(&self) -> Rc<dyn Vector> {
304 let iv = self.curr_iv();
305 {
306 let cache = self.curr_slack_s_u_cache.borrow();
307 if let Some(v) = cache.get(&[iv.s.as_tagged()], &[]) {
308 return v;
309 }
310 }
311 let nlp = self.nlp.borrow();
312 let (v, _) = self.safe_slack_u(&*nlp.pd_u(), &*iv.s, nlp.d_u(), &*iv.v_u);
313 self.curr_slack_s_u_cache
314 .borrow_mut()
315 .add(v.clone(), &[iv.s.as_tagged()], &[]);
316 v
317 }
318
319 pub fn trial_slack_x_l(&self) -> Rc<dyn Vector> {
320 let iv = self.trial_iv();
321 let mult = self.curr_iv();
322 let nlp = self.nlp.borrow();
323 self.safe_slack_l(&*nlp.px_l(), &*iv.x, nlp.x_l(), &*mult.z_l)
324 .0
325 }
326
327 pub fn trial_slack_x_u(&self) -> Rc<dyn Vector> {
328 let iv = self.trial_iv();
329 let mult = self.curr_iv();
330 let nlp = self.nlp.borrow();
331 self.safe_slack_u(&*nlp.px_u(), &*iv.x, nlp.x_u(), &*mult.z_u)
332 .0
333 }
334
335 pub fn trial_slack_s_l(&self) -> Rc<dyn Vector> {
336 let iv = self.trial_iv();
337 let mult = self.curr_iv();
338 let nlp = self.nlp.borrow();
339 self.safe_slack_l(&*nlp.pd_l(), &*iv.s, nlp.d_l(), &*mult.v_l)
340 .0
341 }
342
343 pub fn trial_slack_s_u(&self) -> Rc<dyn Vector> {
344 let iv = self.trial_iv();
345 let mult = self.curr_iv();
346 let nlp = self.nlp.borrow();
347 self.safe_slack_u(&*nlp.pd_u(), &*iv.s, nlp.d_u(), &*mult.v_u)
348 .0
349 }
350
351 pub fn adjusted_trial_bounds(&self) -> Option<AdjustedBounds> {
359 let iv = self.trial_iv();
360 let mult = self.curr_iv();
361 let nlp = self.nlp.borrow();
362
363 let (s_x_l, n_x_l) = self.safe_slack_l(&*nlp.px_l(), &*iv.x, nlp.x_l(), &*mult.z_l);
364 let (s_x_u, n_x_u) = self.safe_slack_u(&*nlp.px_u(), &*iv.x, nlp.x_u(), &*mult.z_u);
365 let (s_s_l, n_s_l) = self.safe_slack_l(&*nlp.pd_l(), &*iv.s, nlp.d_l(), &*mult.v_l);
366 let (s_s_u, n_s_u) = self.safe_slack_u(&*nlp.pd_u(), &*iv.s, nlp.d_u(), &*mult.v_u);
367
368 let adjusted = n_x_l + n_x_u + n_s_l + n_s_u;
369 if adjusted == 0 {
370 return None;
371 }
372
373 let mut new_x_l = nlp.x_l().make_new();
375 nlp.px_l()
376 .trans_mult_vector(1.0, &*iv.x, 0.0, &mut *new_x_l);
377 new_x_l.axpy(-1.0, &*s_x_l);
378 let mut new_x_u = nlp.x_u().make_new();
380 nlp.px_u()
381 .trans_mult_vector(1.0, &*iv.x, 0.0, &mut *new_x_u);
382 new_x_u.axpy(1.0, &*s_x_u);
383 let mut new_d_l = nlp.d_l().make_new();
385 nlp.pd_l()
386 .trans_mult_vector(1.0, &*iv.s, 0.0, &mut *new_d_l);
387 new_d_l.axpy(-1.0, &*s_s_l);
388 let mut new_d_u = nlp.d_u().make_new();
390 nlp.pd_u()
391 .trans_mult_vector(1.0, &*iv.s, 0.0, &mut *new_d_u);
392 new_d_u.axpy(1.0, &*s_s_u);
393
394 Some(AdjustedBounds {
395 adjusted,
396 x_l: new_x_l,
397 x_u: new_x_u,
398 d_l: new_d_l,
399 d_u: new_d_u,
400 })
401 }
402
403 pub fn curr_grad_f(&self) -> Rc<dyn Vector> {
408 let iv = self.curr_iv();
409 let mut nlp = self.nlp.borrow_mut();
410 let mut g = iv.x.make_new();
411 nlp.eval_grad_f(&*iv.x, &mut *g);
412 rc_from(g)
413 }
414
415 pub fn trial_grad_f(&self) -> Rc<dyn Vector> {
416 let iv = self.trial_iv();
417 let mut nlp = self.nlp.borrow_mut();
418 let mut g = iv.x.make_new();
419 nlp.eval_grad_f(&*iv.x, &mut *g);
420 rc_from(g)
421 }
422
423 pub fn curr_c(&self) -> Rc<dyn Vector> {
424 let iv = self.curr_iv();
425 let m = self.nlp.borrow().m_eq();
426 let mut nlp = self.nlp.borrow_mut();
427 let mut c = iv.y_c.make_new();
428 debug_assert_eq!(c.dim(), m);
429 nlp.eval_c(&*iv.x, &mut *c);
430 rc_from(c)
431 }
432
433 pub fn trial_c(&self) -> Rc<dyn Vector> {
434 let iv = self.trial_iv();
435 let mut nlp = self.nlp.borrow_mut();
436 let mut c = iv.y_c.make_new();
437 nlp.eval_c(&*iv.x, &mut *c);
438 rc_from(c)
439 }
440
441 pub fn curr_d(&self) -> Rc<dyn Vector> {
442 let iv = self.curr_iv();
443 let mut nlp = self.nlp.borrow_mut();
444 let mut d = iv.s.make_new();
445 nlp.eval_d(&*iv.x, &mut *d);
446 rc_from(d)
447 }
448
449 pub fn trial_d(&self) -> Rc<dyn Vector> {
450 let iv = self.trial_iv();
451 let mut nlp = self.nlp.borrow_mut();
452 let mut d = iv.s.make_new();
453 nlp.eval_d(&*iv.x, &mut *d);
454 rc_from(d)
455 }
456
457 pub fn curr_jac_c(&self) -> Rc<dyn Matrix> {
458 let iv = self.curr_iv();
459 self.nlp.borrow_mut().eval_jac_c(&*iv.x)
460 }
461
462 pub fn curr_jac_d(&self) -> Rc<dyn Matrix> {
463 let iv = self.curr_iv();
464 self.nlp.borrow_mut().eval_jac_d(&*iv.x)
465 }
466
467 pub fn curr_exact_hessian(&self) -> Rc<dyn SymMatrix> {
468 let iv = self.curr_iv();
469 self.nlp
470 .borrow_mut()
471 .eval_h(&*iv.x, 1.0, &*iv.y_c, &*iv.y_d)
472 }
473
474 pub fn curr_d_minus_s(&self) -> Rc<dyn Vector> {
476 let iv = self.curr_iv();
477 let d = self.curr_d();
478 let mut tmp = iv.s.make_new();
479 tmp.add_two_vectors(1.0, &*d, -1.0, &*iv.s, 0.0);
481 rc_from(tmp)
482 }
483
484 pub fn trial_d_minus_s(&self) -> Rc<dyn Vector> {
485 let iv = self.trial_iv();
486 let d = self.trial_d();
487 let mut tmp = iv.s.make_new();
488 tmp.add_two_vectors(1.0, &*d, -1.0, &*iv.s, 0.0);
489 rc_from(tmp)
490 }
491
492 pub fn curr_jac_c_t_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector> {
495 let iv = self.curr_iv();
496 let jac_c = self.curr_jac_c();
497 let mut tmp = iv.x.make_new();
498 jac_c.trans_mult_vector(1.0, vec, 0.0, &mut *tmp);
499 rc_from(tmp)
500 }
501
502 pub fn curr_jac_d_t_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector> {
504 let iv = self.curr_iv();
505 let jac_d = self.curr_jac_d();
506 let mut tmp = iv.x.make_new();
507 jac_d.trans_mult_vector(1.0, vec, 0.0, &mut *tmp);
508 rc_from(tmp)
509 }
510
511 pub fn curr_jac_c_t_times_curr_y_c(&self) -> Rc<dyn Vector> {
512 let iv = self.curr_iv();
513 self.curr_jac_c_t_times_vec(&*iv.y_c)
514 }
515
516 pub fn curr_jac_d_t_times_curr_y_d(&self) -> Rc<dyn Vector> {
517 let iv = self.curr_iv();
518 self.curr_jac_d_t_times_vec(&*iv.y_d)
519 }
520
521 pub fn curr_jac_c_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector> {
523 let iv = self.curr_iv();
524 let jac_c = self.curr_jac_c();
525 let mut tmp = iv.y_c.make_new();
526 jac_c.mult_vector(1.0, vec, 0.0, &mut *tmp);
527 rc_from(tmp)
528 }
529
530 pub fn curr_jac_d_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector> {
532 let iv = self.curr_iv();
533 let jac_d = self.curr_jac_d();
534 let mut tmp = iv.s.make_new();
535 jac_d.mult_vector(1.0, vec, 0.0, &mut *tmp);
536 rc_from(tmp)
537 }
538
539 pub fn curr_grad_lag_x(&self) -> Rc<dyn Vector> {
546 let iv = self.curr_iv();
547 let grad_f = self.curr_grad_f();
548 let jc_t_y_c = self.curr_jac_c_t_times_curr_y_c();
549 let jd_t_y_d = self.curr_jac_d_t_times_curr_y_d();
550
551 let mut tmp = iv.x.make_new();
552 tmp.copy(&*grad_f);
553 tmp.add_two_vectors(1.0, &*jc_t_y_c, 1.0, &*jd_t_y_d, 1.0);
554
555 let nlp = self.nlp.borrow();
556 nlp.px_l().mult_vector(-1.0, &*iv.z_l, 1.0, &mut *tmp);
557 nlp.px_u().mult_vector(1.0, &*iv.z_u, 1.0, &mut *tmp);
558 rc_from(tmp)
559 }
560
561 pub fn curr_grad_lag_s(&self) -> Rc<dyn Vector> {
564 let iv = self.curr_iv();
565 let mut tmp = iv.y_d.make_new();
566 let nlp = self.nlp.borrow();
567 nlp.pd_u().mult_vector(1.0, &*iv.v_u, 0.0, &mut *tmp);
569 nlp.pd_l().mult_vector(-1.0, &*iv.v_l, 1.0, &mut *tmp);
571 tmp.axpy(-1.0, &*iv.y_d);
573 rc_from(tmp)
574 }
575
576 fn calc_compl(slack: &dyn Vector, mult: &dyn Vector) -> Rc<dyn Vector> {
581 let mut result = slack.make_new();
582 result.copy(slack);
583 result.element_wise_multiply(mult);
584 rc_from(result)
585 }
586
587 pub fn curr_compl_x_l(&self) -> Rc<dyn Vector> {
588 let slack = self.curr_slack_x_l();
589 let z_l = self.curr_iv().z_l;
590 Self::calc_compl(&*slack, &*z_l)
591 }
592
593 pub fn curr_compl_x_u(&self) -> Rc<dyn Vector> {
594 let slack = self.curr_slack_x_u();
595 let z_u = self.curr_iv().z_u;
596 Self::calc_compl(&*slack, &*z_u)
597 }
598
599 pub fn curr_compl_s_l(&self) -> Rc<dyn Vector> {
600 let slack = self.curr_slack_s_l();
601 let v_l = self.curr_iv().v_l;
602 Self::calc_compl(&*slack, &*v_l)
603 }
604
605 pub fn curr_compl_s_u(&self) -> Rc<dyn Vector> {
606 let slack = self.curr_slack_s_u();
607 let v_u = self.curr_iv().v_u;
608 Self::calc_compl(&*slack, &*v_u)
609 }
610
611 pub fn curr_relaxed_compl_x_l(&self) -> Rc<dyn Vector> {
614 let mu = self.data.borrow().curr_mu;
615 let mut r = self.curr_compl_x_l().make_new();
616 r.copy(&*self.curr_compl_x_l());
617 r.add_scalar(-mu);
618 rc_from(r)
619 }
620
621 pub fn curr_relaxed_compl_x_u(&self) -> Rc<dyn Vector> {
622 let mu = self.data.borrow().curr_mu;
623 let mut r = self.curr_compl_x_u().make_new();
624 r.copy(&*self.curr_compl_x_u());
625 r.add_scalar(-mu);
626 rc_from(r)
627 }
628
629 pub fn curr_relaxed_compl_s_l(&self) -> Rc<dyn Vector> {
630 let mu = self.data.borrow().curr_mu;
631 let mut r = self.curr_compl_s_l().make_new();
632 r.copy(&*self.curr_compl_s_l());
633 r.add_scalar(-mu);
634 rc_from(r)
635 }
636
637 pub fn curr_relaxed_compl_s_u(&self) -> Rc<dyn Vector> {
638 let mu = self.data.borrow().curr_mu;
639 let mut r = self.curr_compl_s_u().make_new();
640 r.copy(&*self.curr_compl_s_u());
641 r.add_scalar(-mu);
642 rc_from(r)
643 }
644
645 pub fn curr_sigma_x(&self) -> Rc<dyn Vector> {
654 let iv = self.curr_iv();
655 {
656 let cache = self.curr_sigma_x_cache.borrow();
657 if let Some(v) = cache.get(
658 &[iv.x.as_tagged(), iv.z_l.as_tagged(), iv.z_u.as_tagged()],
659 &[],
660 ) {
661 return v;
662 }
663 }
664 let slack_l = self.curr_slack_x_l();
665 let slack_u = self.curr_slack_x_u();
666
667 let mut sigma = iv.x.make_new();
668 sigma.set(0.0);
669
670 let nlp = self.nlp.borrow();
671 nlp.px_l()
672 .add_m_sinv_z(1.0, &*slack_l, &*iv.z_l, &mut *sigma);
673 nlp.px_u()
674 .add_m_sinv_z(1.0, &*slack_u, &*iv.z_u, &mut *sigma);
675 let v = rc_from(sigma);
676 self.curr_sigma_x_cache.borrow_mut().add(
677 v.clone(),
678 &[iv.x.as_tagged(), iv.z_l.as_tagged(), iv.z_u.as_tagged()],
679 &[],
680 );
681 v
682 }
683
684 pub fn curr_sigma_s(&self) -> Rc<dyn Vector> {
685 let iv = self.curr_iv();
686 {
687 let cache = self.curr_sigma_s_cache.borrow();
688 if let Some(v) = cache.get(
689 &[iv.s.as_tagged(), iv.v_l.as_tagged(), iv.v_u.as_tagged()],
690 &[],
691 ) {
692 return v;
693 }
694 }
695 let slack_l = self.curr_slack_s_l();
696 let slack_u = self.curr_slack_s_u();
697
698 let mut sigma = iv.s.make_new();
699 sigma.set(0.0);
700
701 let nlp = self.nlp.borrow();
702 nlp.pd_l()
703 .add_m_sinv_z(1.0, &*slack_l, &*iv.v_l, &mut *sigma);
704 nlp.pd_u()
705 .add_m_sinv_z(1.0, &*slack_u, &*iv.v_u, &mut *sigma);
706 let v = rc_from(sigma);
707 self.curr_sigma_s_cache.borrow_mut().add(
708 v.clone(),
709 &[iv.s.as_tagged(), iv.v_l.as_tagged(), iv.v_u.as_tagged()],
710 &[],
711 );
712 v
713 }
714
715 pub fn curr_f(&self) -> Number {
732 let iv = self.curr_iv();
733 let mut nlp = self.nlp.borrow_mut();
734 nlp.eval_f(&*iv.x)
735 }
736
737 pub fn unscaled_curr_f(&self) -> Number {
744 let scaled = self.curr_f();
745 let factor = self.nlp.borrow().obj_scaling_factor();
746 if factor == 0.0 {
747 scaled
748 } else {
749 scaled / factor
750 }
751 }
752
753 pub fn trial_f(&self) -> Number {
754 let iv = self.trial_iv();
755 let mut nlp = self.nlp.borrow_mut();
756 nlp.eval_f(&*iv.x)
757 }
758
759 fn barrier_obj_at(
760 &self,
761 f: Number,
762 s_x_l: &dyn Vector,
763 s_x_u: &dyn Vector,
764 s_s_l: &dyn Vector,
765 s_s_u: &dyn Vector,
766 ) -> Number {
767 let mu = self.data.borrow().curr_mu;
768 let log_sum = s_x_l.sum_logs() + s_x_u.sum_logs() + s_s_l.sum_logs() + s_s_u.sum_logs();
769 let mut phi = f - mu * log_sum;
770 if self.kappa_d > 0.0 {
771 let di = self.damping_indicators();
772 phi += self.kappa_d * mu * s_x_l.dot(&*di.x_l);
773 phi += self.kappa_d * mu * s_x_u.dot(&*di.x_u);
774 phi += self.kappa_d * mu * s_s_l.dot(&*di.s_l);
775 phi += self.kappa_d * mu * s_s_u.dot(&*di.s_u);
776 }
777 phi
778 }
779
780 pub fn curr_barrier_obj(&self) -> Number {
781 let f = self.curr_f();
782 let s_x_l = self.curr_slack_x_l();
783 let s_x_u = self.curr_slack_x_u();
784 let s_s_l = self.curr_slack_s_l();
785 let s_s_u = self.curr_slack_s_u();
786 self.barrier_obj_at(f, &*s_x_l, &*s_x_u, &*s_s_l, &*s_s_u)
787 }
788
789 pub fn trial_barrier_obj(&self) -> Number {
790 let f = self.trial_f();
791 let s_x_l = self.trial_slack_x_l();
792 let s_x_u = self.trial_slack_x_u();
793 let s_s_l = self.trial_slack_s_l();
794 let s_s_u = self.trial_slack_s_u();
795 self.barrier_obj_at(f, &*s_x_l, &*s_x_u, &*s_s_l, &*s_s_u)
796 }
797
798 pub fn curr_grad_barrier_obj_x(&self) -> Rc<dyn Vector> {
802 let iv = self.curr_iv();
803 let mu = self.data.borrow().curr_mu;
804 let s_l = self.curr_slack_x_l();
805 let s_u = self.curr_slack_x_u();
806
807 let mut inv_s_l = s_l.make_new();
808 inv_s_l.copy(&*s_l);
809 inv_s_l.element_wise_reciprocal();
810 let mut inv_s_u = s_u.make_new();
811 inv_s_u.copy(&*s_u);
812 inv_s_u.element_wise_reciprocal();
813
814 let grad_f = self.curr_grad_f();
815 let mut tmp = iv.x.make_new();
816 tmp.copy(&*grad_f);
817 let nlp = self.nlp.borrow();
818 nlp.px_l().mult_vector(-mu, &*inv_s_l, 1.0, &mut *tmp);
820 nlp.px_u().mult_vector(mu, &*inv_s_u, 1.0, &mut *tmp);
822
823 if self.kappa_d > 0.0 {
824 let di = self.damping_indicators();
825 nlp.px_l()
827 .mult_vector(self.kappa_d * mu, &*di.x_l, 1.0, &mut *tmp);
828 nlp.px_u()
830 .mult_vector(-self.kappa_d * mu, &*di.x_u, 1.0, &mut *tmp);
831 }
832 rc_from(tmp)
833 }
834
835 pub fn curr_grad_barrier_obj_s(&self) -> Rc<dyn Vector> {
838 let iv = self.curr_iv();
839 let mu = self.data.borrow().curr_mu;
840 let s_l = self.curr_slack_s_l();
841 let s_u = self.curr_slack_s_u();
842
843 let mut inv_s_l = s_l.make_new();
844 inv_s_l.copy(&*s_l);
845 inv_s_l.element_wise_reciprocal();
846 let mut inv_s_u = s_u.make_new();
847 inv_s_u.copy(&*s_u);
848 inv_s_u.element_wise_reciprocal();
849
850 let mut tmp = iv.s.make_new();
851 tmp.set(0.0);
852 let nlp = self.nlp.borrow();
853 nlp.pd_l().mult_vector(-mu, &*inv_s_l, 1.0, &mut *tmp);
854 nlp.pd_u().mult_vector(mu, &*inv_s_u, 1.0, &mut *tmp);
855
856 if self.kappa_d > 0.0 {
857 let di = self.damping_indicators();
858 nlp.pd_l()
859 .mult_vector(self.kappa_d * mu, &*di.s_l, 1.0, &mut *tmp);
860 nlp.pd_u()
861 .mult_vector(-self.kappa_d * mu, &*di.s_u, 1.0, &mut *tmp);
862 }
863 rc_from(tmp)
864 }
865
866 pub fn curr_grad_barr_t_delta(&self, delta_x: &dyn Vector, delta_s: &dyn Vector) -> Number {
878 let g_x = self.curr_grad_barrier_obj_x();
879 let g_s = self.curr_grad_barrier_obj_s();
880 g_x.dot(delta_x) + g_s.dot(delta_s)
881 }
882
883 pub fn curr_dwd(&self, delta_x: &dyn Vector, delta_s: &dyn Vector) -> Number {
890 let mut dwd: Number = 0.0;
891
892 if let Some(w) = self.data.borrow().w.clone() {
894 let mut wd = delta_x.make_new();
895 w.mult_vector(1.0, delta_x, 0.0, &mut *wd);
896 dwd += wd.dot(delta_x);
897 }
898
899 let sigma_x = self.curr_sigma_x();
901 let mut tmp_x = delta_x.make_new();
902 tmp_x.copy(delta_x);
903 tmp_x.element_wise_multiply(&*sigma_x);
904 dwd += tmp_x.dot(delta_x);
905
906 let sigma_s = self.curr_sigma_s();
908 let mut tmp_s = delta_s.make_new();
909 tmp_s.copy(delta_s);
910 tmp_s.element_wise_multiply(&*sigma_s);
911 dwd += tmp_s.dot(delta_s);
912
913 let pert = self.data.borrow().perturbations;
915 if pert.delta_x != 0.0 {
916 let nx = delta_x.nrm2();
917 dwd += pert.delta_x * nx * nx;
918 }
919 if pert.delta_s != 0.0 {
920 let ns = delta_s.nrm2();
921 dwd += pert.delta_s * ns * ns;
922 }
923
924 dwd.max(0.0)
925 }
926
927 pub fn curr_constraint_violation(&self) -> Number {
935 let c = self.curr_c();
936 let dms = self.curr_d_minus_s();
937 c.asum() + dms.asum()
938 }
939
940 pub fn trial_constraint_violation(&self) -> Number {
941 let c = self.trial_c();
942 let dms = self.trial_d_minus_s();
943 c.asum() + dms.asum()
944 }
945
946 pub fn curr_primal_infeasibility_max(&self) -> Number {
951 let c = self.curr_c();
952 let dms = self.curr_d_minus_s();
953 c.amax().max(dms.amax())
954 }
955
956 pub fn curr_dual_infeasibility_max(&self) -> Number {
959 let glx = self.curr_grad_lag_x();
960 let gls = self.curr_grad_lag_s();
961 glx.amax().max(gls.amax())
962 }
963
964 pub fn curr_infeasibility_stationarity(&self) -> Number {
974 let c = self.curr_c();
975 let dms = self.curr_d_minus_s();
976 let jc_t_c = self.curr_jac_c_t_times_vec(&*c);
977 let jd_t_dms = self.curr_jac_d_t_times_vec(&*dms);
978 let mut grad = jc_t_c.make_new();
979 grad.add_two_vectors(1.0, &*jc_t_c, 1.0, &*jd_t_dms, 0.0);
980 let viol = c.amax().max(dms.amax());
981 grad.amax() / viol.max(1.0)
982 }
983
984 pub fn curr_avrg_compl(&self) -> Number {
992 let iv = self.curr_iv();
993 let n = iv.z_l.dim() + iv.z_u.dim() + iv.v_l.dim() + iv.v_u.dim();
994 if n == 0 {
995 return 0.0;
996 }
997 let s_x_l = self.curr_slack_x_l();
998 let s_x_u = self.curr_slack_x_u();
999 let s_s_l = self.curr_slack_s_l();
1000 let s_s_u = self.curr_slack_s_u();
1001 let mut acc = iv.z_l.dot(&*s_x_l);
1002 acc += iv.z_u.dot(&*s_x_u);
1003 acc += iv.v_l.dot(&*s_s_l);
1004 acc += iv.v_u.dot(&*s_s_u);
1005 acc / Number::from(n)
1006 }
1007
1008 pub fn curr_complementarity_min(&self) -> Number {
1015 let cxl = self.curr_compl_x_l();
1016 let cxu = self.curr_compl_x_u();
1017 let csl = self.curr_compl_s_l();
1018 let csu = self.curr_compl_s_u();
1019 let m = |v: &Rc<dyn Vector>| {
1020 if v.dim() == 0 {
1021 Number::INFINITY
1022 } else {
1023 v.min()
1024 }
1025 };
1026 let acc = m(&cxl).min(m(&cxu)).min(m(&csl)).min(m(&csu));
1027 if acc.is_infinite() {
1028 0.0
1029 } else {
1030 acc
1031 }
1032 }
1033
1034 pub fn curr_complementarity_max(&self) -> Number {
1042 self.curr_compl_x_l()
1043 .amax()
1044 .max(self.curr_compl_x_u().amax())
1045 .max(self.curr_compl_s_l().amax())
1046 .max(self.curr_compl_s_u().amax())
1047 }
1048
1049 pub fn curr_centrality_measure(&self) -> Number {
1055 let avrg = self.curr_avrg_compl();
1056 if avrg <= 0.0 {
1057 return 1.0;
1058 }
1059 self.curr_complementarity_min() / avrg
1060 }
1061
1062 pub fn curr_barrier_error(&self) -> Number {
1070 let iv = self.curr_iv();
1071 let (s_d, s_c) = self.optimality_error_scaling(&iv);
1072
1073 let glx = self.curr_grad_lag_x();
1074 let gls = self.curr_grad_lag_s();
1075 let dual = glx.amax().max(gls.amax()) / s_d;
1076
1077 let c = self.curr_c();
1078 let dms = self.curr_d_minus_s();
1079 let primal = c.amax().max(dms.amax());
1080
1081 let compl = self
1082 .curr_relaxed_compl_x_l()
1083 .amax()
1084 .max(self.curr_relaxed_compl_x_u().amax())
1085 .max(self.curr_relaxed_compl_s_l().amax())
1086 .max(self.curr_relaxed_compl_s_u().amax())
1087 / s_c;
1088
1089 dual.max(primal).max(compl)
1090 }
1091
1092 pub fn curr_nlp_error(&self) -> Number {
1106 let iv = self.curr_iv();
1107 let (s_d, s_c) = self.optimality_error_scaling(&iv);
1108
1109 let glx = self.curr_grad_lag_x();
1111 let gls = self.curr_grad_lag_s();
1112 let dual = glx.amax().max(gls.amax()) / s_d;
1113
1114 let c = self.curr_c();
1116 let dms = self.curr_d_minus_s();
1117 let primal = c.amax().max(dms.amax());
1118
1119 let compl = self
1121 .curr_compl_x_l()
1122 .amax()
1123 .max(self.curr_compl_x_u().amax())
1124 .max(self.curr_compl_s_l().amax())
1125 .max(self.curr_compl_s_u().amax())
1126 / s_c;
1127
1128 dual.max(primal).max(compl)
1129 }
1130
1131 fn optimality_error_scaling(&self, iv: &IteratesVector) -> (Number, Number) {
1134 let s_max = self.s_max;
1135
1136 let n_c = iv.z_l.dim() + iv.z_u.dim() + iv.v_l.dim() + iv.v_u.dim();
1139 let s_c = if n_c == 0 {
1140 1.0
1141 } else {
1142 let asum = iv.z_l.asum() + iv.z_u.asum() + iv.v_l.asum() + iv.v_u.asum();
1143 (s_max.max(asum / Number::from(n_c))) / s_max
1144 };
1145
1146 let n_d =
1148 iv.y_c.dim() + iv.y_d.dim() + iv.z_l.dim() + iv.z_u.dim() + iv.v_l.dim() + iv.v_u.dim();
1149 let s_d = if n_d == 0 {
1150 1.0
1151 } else {
1152 let asum = iv.y_c.asum()
1153 + iv.y_d.asum()
1154 + iv.z_l.asum()
1155 + iv.z_u.asum()
1156 + iv.v_l.asum()
1157 + iv.v_u.asum();
1158 (s_max.max(asum / Number::from(n_d))) / s_max
1159 };
1160
1161 (s_d, s_c)
1162 }
1163
1164 pub fn trial_jac_c(&self) -> Rc<dyn Matrix> {
1172 let iv = self.trial_iv();
1173 self.nlp.borrow_mut().eval_jac_c(&*iv.x)
1174 }
1175
1176 pub fn trial_jac_d(&self) -> Rc<dyn Matrix> {
1177 let iv = self.trial_iv();
1178 self.nlp.borrow_mut().eval_jac_d(&*iv.x)
1179 }
1180
1181 pub fn trial_grad_lag_x(&self) -> Rc<dyn Vector> {
1183 let iv = self.trial_iv();
1184 let grad_f = self.trial_grad_f();
1185 let jac_c = self.trial_jac_c();
1186 let jac_d = self.trial_jac_d();
1187
1188 let mut jc_t = iv.x.make_new();
1189 jac_c.trans_mult_vector(1.0, &*iv.y_c, 0.0, &mut *jc_t);
1190 let mut jd_t = iv.x.make_new();
1191 jac_d.trans_mult_vector(1.0, &*iv.y_d, 0.0, &mut *jd_t);
1192
1193 let mut tmp = iv.x.make_new();
1194 tmp.copy(&*grad_f);
1195 tmp.add_two_vectors(1.0, &*jc_t, 1.0, &*jd_t, 1.0);
1196
1197 let nlp = self.nlp.borrow();
1198 nlp.px_l().mult_vector(-1.0, &*iv.z_l, 1.0, &mut *tmp);
1199 nlp.px_u().mult_vector(1.0, &*iv.z_u, 1.0, &mut *tmp);
1200 rc_from(tmp)
1201 }
1202
1203 pub fn trial_grad_lag_s(&self) -> Rc<dyn Vector> {
1205 let iv = self.trial_iv();
1206 let mut tmp = iv.y_d.make_new();
1207 let nlp = self.nlp.borrow();
1208 nlp.pd_u().mult_vector(1.0, &*iv.v_u, 0.0, &mut *tmp);
1209 nlp.pd_l().mult_vector(-1.0, &*iv.v_l, 1.0, &mut *tmp);
1210 tmp.axpy(-1.0, &*iv.y_d);
1211 rc_from(tmp)
1212 }
1213
1214 pub fn trial_compl_x_l(&self) -> Rc<dyn Vector> {
1215 Self::calc_compl(&*self.trial_slack_x_l(), &*self.trial_iv().z_l)
1216 }
1217
1218 pub fn trial_compl_x_u(&self) -> Rc<dyn Vector> {
1219 Self::calc_compl(&*self.trial_slack_x_u(), &*self.trial_iv().z_u)
1220 }
1221
1222 pub fn trial_compl_s_l(&self) -> Rc<dyn Vector> {
1223 Self::calc_compl(&*self.trial_slack_s_l(), &*self.trial_iv().v_l)
1224 }
1225
1226 pub fn trial_compl_s_u(&self) -> Rc<dyn Vector> {
1227 Self::calc_compl(&*self.trial_slack_s_u(), &*self.trial_iv().v_u)
1228 }
1229
1230 fn relaxed_compl_asum(blocks: &[Rc<dyn Vector>], mu: Number) -> Number {
1232 let mut acc = 0.0;
1233 for compl in blocks {
1234 if compl.dim() == 0 {
1235 continue;
1236 }
1237 let mut r = compl.make_new();
1238 r.copy(&**compl);
1239 r.add_scalar(-mu);
1240 acc += r.asum();
1241 }
1242 acc
1243 }
1244
1245 pub fn curr_primal_dual_system_error(&self, mu: Number) -> Number {
1253 let iv = self.curr_iv();
1254 let n_dual = iv.x.dim() + iv.s.dim();
1255 let dual_inf =
1256 (self.curr_grad_lag_x().asum() + self.curr_grad_lag_s().asum()) / Number::from(n_dual);
1257
1258 let n_primal = iv.y_c.dim() + iv.y_d.dim();
1259 let primal_inf = if n_primal > 0 {
1260 (self.curr_c().asum() + self.curr_d_minus_s().asum()) / Number::from(n_primal)
1261 } else {
1262 0.0
1263 };
1264
1265 let n_cmpl = iv.z_l.dim() + iv.z_u.dim() + iv.v_l.dim() + iv.v_u.dim();
1266 let cmpl = if n_cmpl > 0 {
1267 Self::relaxed_compl_asum(
1268 &[
1269 self.curr_compl_x_l(),
1270 self.curr_compl_x_u(),
1271 self.curr_compl_s_l(),
1272 self.curr_compl_s_u(),
1273 ],
1274 mu,
1275 ) / Number::from(n_cmpl)
1276 } else {
1277 0.0
1278 };
1279
1280 dual_inf + primal_inf + cmpl
1281 }
1282
1283 pub fn trial_primal_dual_system_error(&self, mu: Number) -> Number {
1286 let iv = self.trial_iv();
1287 let n_dual = iv.x.dim() + iv.s.dim();
1288 let dual_inf = (self.trial_grad_lag_x().asum() + self.trial_grad_lag_s().asum())
1289 / Number::from(n_dual);
1290
1291 let n_primal = iv.y_c.dim() + iv.y_d.dim();
1292 let primal_inf = if n_primal > 0 {
1293 (self.trial_c().asum() + self.trial_d_minus_s().asum()) / Number::from(n_primal)
1294 } else {
1295 0.0
1296 };
1297
1298 let n_cmpl = iv.z_l.dim() + iv.z_u.dim() + iv.v_l.dim() + iv.v_u.dim();
1299 let cmpl = if n_cmpl > 0 {
1300 Self::relaxed_compl_asum(
1301 &[
1302 self.trial_compl_x_l(),
1303 self.trial_compl_x_u(),
1304 self.trial_compl_s_l(),
1305 self.trial_compl_s_u(),
1306 ],
1307 mu,
1308 ) / Number::from(n_cmpl)
1309 } else {
1310 0.0
1311 };
1312
1313 dual_inf + primal_inf + cmpl
1314 }
1315
1316 fn damping_indicators(&self) -> DampingIndicators {
1327 let nlp = self.nlp.borrow();
1328
1329 let mut tmp_x_l = nlp.x_l().make_new();
1330 tmp_x_l.set(1.0);
1331 let mut tmp_x_u = nlp.x_u().make_new();
1332 tmp_x_u.set(1.0);
1333 let mut tmp_x = self.curr_iv().x.make_new();
1334 nlp.px_l().mult_vector(1.0, &*tmp_x_l, 0.0, &mut *tmp_x);
1335 nlp.px_u().mult_vector(-1.0, &*tmp_x_u, 1.0, &mut *tmp_x);
1336 let mut d_x_l = nlp.x_l().make_new();
1337 nlp.px_l().trans_mult_vector(1.0, &*tmp_x, 0.0, &mut *d_x_l);
1338 let mut d_x_u = nlp.x_u().make_new();
1339 nlp.px_u()
1340 .trans_mult_vector(-1.0, &*tmp_x, 0.0, &mut *d_x_u);
1341
1342 let mut tmp_s_l = nlp.d_l().make_new();
1343 tmp_s_l.set(1.0);
1344 let mut tmp_s_u = nlp.d_u().make_new();
1345 tmp_s_u.set(1.0);
1346 let mut tmp_s = self.curr_iv().s.make_new();
1347 nlp.pd_l().mult_vector(1.0, &*tmp_s_l, 0.0, &mut *tmp_s);
1348 nlp.pd_u().mult_vector(-1.0, &*tmp_s_u, 1.0, &mut *tmp_s);
1349 let mut d_s_l = nlp.d_l().make_new();
1350 nlp.pd_l().trans_mult_vector(1.0, &*tmp_s, 0.0, &mut *d_s_l);
1351 let mut d_s_u = nlp.d_u().make_new();
1352 nlp.pd_u()
1353 .trans_mult_vector(-1.0, &*tmp_s, 0.0, &mut *d_s_u);
1354
1355 DampingIndicators {
1356 x_l: rc_from(d_x_l),
1357 x_u: rc_from(d_x_u),
1358 s_l: rc_from(d_s_l),
1359 s_u: rc_from(d_s_u),
1360 }
1361 }
1362
1363 pub fn curr_grad_lag_with_damping_x(&self) -> Rc<dyn Vector> {
1368 if self.kappa_d == 0.0 {
1369 return self.curr_grad_lag_x();
1370 }
1371 let mu = self.data.borrow().curr_mu;
1372 let di = self.damping_indicators();
1373 let (d_x_l, d_x_u) = (di.x_l, di.x_u);
1374 let glx = self.curr_grad_lag_x();
1375 let mut tmp = glx.make_new();
1376 tmp.copy(&*glx);
1377 let nlp = self.nlp.borrow();
1378 nlp.px_l()
1379 .mult_vector(self.kappa_d * mu, &*d_x_l, 1.0, &mut *tmp);
1380 nlp.px_u()
1381 .mult_vector(-self.kappa_d * mu, &*d_x_u, 1.0, &mut *tmp);
1382 rc_from(tmp)
1383 }
1384
1385 pub fn curr_grad_lag_with_damping_s(&self) -> Rc<dyn Vector> {
1386 if self.kappa_d == 0.0 {
1387 return self.curr_grad_lag_s();
1388 }
1389 let mu = self.data.borrow().curr_mu;
1390 let di = self.damping_indicators();
1391 let (d_s_l, d_s_u) = (di.s_l, di.s_u);
1392 let gls = self.curr_grad_lag_s();
1393 let mut tmp = gls.make_new();
1394 tmp.copy(&*gls);
1395 let nlp = self.nlp.borrow();
1396 nlp.pd_l()
1397 .mult_vector(self.kappa_d * mu, &*d_s_l, 1.0, &mut *tmp);
1398 nlp.pd_u()
1399 .mult_vector(-self.kappa_d * mu, &*d_s_u, 1.0, &mut *tmp);
1400 rc_from(tmp)
1401 }
1402
1403 pub fn grad_kappa_times_damping_x(&self) -> Rc<dyn Vector> {
1410 let mut tmp = self.curr_iv().x.make_new();
1411 tmp.set(0.0);
1412 if self.kappa_d > 0.0 {
1413 let di = self.damping_indicators();
1414 let nlp = self.nlp.borrow();
1415 nlp.px_l()
1416 .mult_vector(self.kappa_d, &*di.x_l, 0.0, &mut *tmp);
1417 nlp.px_u()
1418 .mult_vector(-self.kappa_d, &*di.x_u, 1.0, &mut *tmp);
1419 }
1420 rc_from(tmp)
1421 }
1422
1423 pub fn grad_kappa_times_damping_s(&self) -> Rc<dyn Vector> {
1424 let mut tmp = self.curr_iv().s.make_new();
1425 tmp.set(0.0);
1426 if self.kappa_d > 0.0 {
1427 let di = self.damping_indicators();
1428 let nlp = self.nlp.borrow();
1429 nlp.pd_l()
1430 .mult_vector(self.kappa_d, &*di.s_l, 0.0, &mut *tmp);
1431 nlp.pd_u()
1432 .mult_vector(-self.kappa_d, &*di.s_u, 1.0, &mut *tmp);
1433 }
1434 rc_from(tmp)
1435 }
1436
1437 pub fn aff_step_alpha_primal_max(&self, delta_aff: &IteratesVector, tau: Number) -> Number {
1450 let nlp = self.nlp.borrow();
1451 let s_x_l = self.curr_slack_x_l();
1452 let s_x_u = self.curr_slack_x_u();
1453 let s_s_l = self.curr_slack_s_l();
1454 let s_s_u = self.curr_slack_s_u();
1455
1456 let mut step_x_l = s_x_l.make_new();
1458 nlp.px_l()
1459 .trans_mult_vector(1.0, &*delta_aff.x, 0.0, &mut *step_x_l);
1460 let mut step_x_u = s_x_u.make_new();
1461 nlp.px_u()
1462 .trans_mult_vector(-1.0, &*delta_aff.x, 0.0, &mut *step_x_u);
1463 let mut step_s_l = s_s_l.make_new();
1464 nlp.pd_l()
1465 .trans_mult_vector(1.0, &*delta_aff.s, 0.0, &mut *step_s_l);
1466 let mut step_s_u = s_s_u.make_new();
1467 nlp.pd_u()
1468 .trans_mult_vector(-1.0, &*delta_aff.s, 0.0, &mut *step_s_u);
1469
1470 s_x_l
1471 .frac_to_bound(&*step_x_l, tau)
1472 .min(s_x_u.frac_to_bound(&*step_x_u, tau))
1473 .min(s_s_l.frac_to_bound(&*step_s_l, tau))
1474 .min(s_s_u.frac_to_bound(&*step_s_u, tau))
1475 }
1476
1477 pub fn aff_step_alpha_dual_max(&self, delta_aff: &IteratesVector, tau: Number) -> Number {
1479 let iv = self.curr_iv();
1480 iv.z_l
1481 .frac_to_bound(&*delta_aff.z_l, tau)
1482 .min(iv.z_u.frac_to_bound(&*delta_aff.z_u, tau))
1483 .min(iv.v_l.frac_to_bound(&*delta_aff.v_l, tau))
1484 .min(iv.v_u.frac_to_bound(&*delta_aff.v_u, tau))
1485 }
1486
1487 pub fn aff_step_compl_avrg(
1491 &self,
1492 delta_aff: &IteratesVector,
1493 alpha_primal: Number,
1494 alpha_dual: Number,
1495 ) -> Number {
1496 let iv = self.curr_iv();
1497 let n = iv.z_l.dim() + iv.z_u.dim() + iv.v_l.dim() + iv.v_u.dim();
1498 if n == 0 {
1499 return 0.0;
1500 }
1501 let nlp = self.nlp.borrow();
1502
1503 let s_x_l = self.curr_slack_x_l();
1505 let mut s_x_l_aff = s_x_l.make_new();
1506 s_x_l_aff.copy(&*s_x_l);
1507 let mut tmp = s_x_l.make_new();
1508 nlp.px_l()
1509 .trans_mult_vector(1.0, &*delta_aff.x, 0.0, &mut *tmp);
1510 s_x_l_aff.axpy(alpha_primal, &*tmp);
1511 let mut z_l_aff = iv.z_l.make_new();
1513 z_l_aff.copy(&*iv.z_l);
1514 z_l_aff.axpy(alpha_dual, &*delta_aff.z_l);
1515 let mut acc = s_x_l_aff.dot(&*z_l_aff);
1516
1517 let s_x_u = self.curr_slack_x_u();
1519 let mut s_x_u_aff = s_x_u.make_new();
1520 s_x_u_aff.copy(&*s_x_u);
1521 let mut tmp = s_x_u.make_new();
1522 nlp.px_u()
1523 .trans_mult_vector(-1.0, &*delta_aff.x, 0.0, &mut *tmp);
1524 s_x_u_aff.axpy(alpha_primal, &*tmp);
1525 let mut z_u_aff = iv.z_u.make_new();
1526 z_u_aff.copy(&*iv.z_u);
1527 z_u_aff.axpy(alpha_dual, &*delta_aff.z_u);
1528 acc += s_x_u_aff.dot(&*z_u_aff);
1529
1530 let s_s_l = self.curr_slack_s_l();
1532 let mut s_s_l_aff = s_s_l.make_new();
1533 s_s_l_aff.copy(&*s_s_l);
1534 let mut tmp = s_s_l.make_new();
1535 nlp.pd_l()
1536 .trans_mult_vector(1.0, &*delta_aff.s, 0.0, &mut *tmp);
1537 s_s_l_aff.axpy(alpha_primal, &*tmp);
1538 let mut v_l_aff = iv.v_l.make_new();
1539 v_l_aff.copy(&*iv.v_l);
1540 v_l_aff.axpy(alpha_dual, &*delta_aff.v_l);
1541 acc += s_s_l_aff.dot(&*v_l_aff);
1542
1543 let s_s_u = self.curr_slack_s_u();
1545 let mut s_s_u_aff = s_s_u.make_new();
1546 s_s_u_aff.copy(&*s_s_u);
1547 let mut tmp = s_s_u.make_new();
1548 nlp.pd_u()
1549 .trans_mult_vector(-1.0, &*delta_aff.s, 0.0, &mut *tmp);
1550 s_s_u_aff.axpy(alpha_primal, &*tmp);
1551 let mut v_u_aff = iv.v_u.make_new();
1552 v_u_aff.copy(&*iv.v_u);
1553 v_u_aff.axpy(alpha_dual, &*delta_aff.v_u);
1554 acc += s_s_u_aff.dot(&*v_u_aff);
1555
1556 acc / Number::from(n)
1557 }
1558}
1559
1560pub type IpoptCqHandle = Rc<RefCell<IpoptCalculatedQuantities>>;
1562
1563struct DampingIndicators {
1567 x_l: Rc<dyn Vector>,
1568 x_u: Rc<dyn Vector>,
1569 s_l: Rc<dyn Vector>,
1570 s_u: Rc<dyn Vector>,
1571}
1572
1573#[cfg(test)]
1574mod tests {
1575 use super::*;
1576 use crate::ipopt_data::IpoptData;
1577 use crate::iterates_vector::IteratesVector;
1578 use pounce_common::types::Index;
1579 use pounce_linalg::dense_vector::{DenseVector, DenseVectorSpace};
1580 use pounce_linalg::expansion_matrix::{ExpansionMatrix, ExpansionMatrixSpace};
1581 use std::rc::Rc as StdRc;
1582
1583 fn dvec(values: &[Number]) -> DenseVector {
1584 let space = DenseVectorSpace::new(values.len() as Index);
1585 let mut v = space.make_new_dense();
1586 v.values_mut().copy_from_slice(values);
1587 v
1588 }
1589
1590 fn rcv(values: &[Number]) -> Rc<dyn Vector> {
1591 StdRc::new(dvec(values))
1592 }
1593
1594 struct MockNlp {
1600 x_l: DenseVector,
1601 x_u: DenseVector,
1602 d_l: DenseVector,
1603 d_u: DenseVector,
1604 px_l: Rc<dyn Matrix>,
1605 px_u: Rc<dyn Matrix>,
1606 pd_l: Rc<dyn Matrix>,
1607 pd_u: Rc<dyn Matrix>,
1608 }
1609
1610 impl MockNlp {
1611 fn new() -> Self {
1612 let x_l = dvec(&[0.0]);
1614 let x_u = dvec(&[5.0]);
1616 let d_l = dvec(&[1.0]);
1618 let d_u = dvec(&[]);
1619
1620 let px_l_space = ExpansionMatrixSpace::new(2, 1, &[0], 0);
1621 let px_u_space = ExpansionMatrixSpace::new(2, 1, &[1], 0);
1622 let pd_l_space = ExpansionMatrixSpace::new(1, 1, &[0], 0);
1623 let pd_u_space = ExpansionMatrixSpace::new(1, 0, &[], 0);
1624
1625 Self {
1626 x_l,
1627 x_u,
1628 d_l,
1629 d_u,
1630 px_l: StdRc::new(ExpansionMatrix::new(px_l_space)),
1631 px_u: StdRc::new(ExpansionMatrix::new(px_u_space)),
1632 pd_l: StdRc::new(ExpansionMatrix::new(pd_l_space)),
1633 pd_u: StdRc::new(ExpansionMatrix::new(pd_u_space)),
1634 }
1635 }
1636 }
1637
1638 impl crate::ipopt_nlp::Nlp for MockNlp {
1639 fn n(&self) -> Index {
1640 2
1641 }
1642 fn m_eq(&self) -> Index {
1643 1
1644 }
1645 fn m_ineq(&self) -> Index {
1646 1
1647 }
1648 fn eval_f(&mut self, x: &dyn Vector) -> Number {
1649 let xx = x.as_any().downcast_ref::<DenseVector>().unwrap();
1651 xx.values()[0] * xx.values()[0] + xx.values()[1] * xx.values()[1]
1652 }
1653 fn eval_grad_f(&mut self, x: &dyn Vector, g: &mut dyn Vector) {
1654 let xx = x.as_any().downcast_ref::<DenseVector>().unwrap();
1656 let gg = g.as_any_mut().downcast_mut::<DenseVector>().unwrap();
1657 gg.values_mut()[0] = 2.0 * xx.values()[0];
1658 gg.values_mut()[1] = 2.0 * xx.values()[1];
1659 }
1660 fn eval_c(&mut self, x: &dyn Vector, c: &mut dyn Vector) {
1661 let xx = x.as_any().downcast_ref::<DenseVector>().unwrap();
1662 let cc = c.as_any_mut().downcast_mut::<DenseVector>().unwrap();
1663 cc.values_mut()[0] = xx.values()[0] + xx.values()[1] - 1.0;
1664 }
1665 fn eval_d(&mut self, x: &dyn Vector, d: &mut dyn Vector) {
1666 let xx = x.as_any().downcast_ref::<DenseVector>().unwrap();
1667 let dd = d.as_any_mut().downcast_mut::<DenseVector>().unwrap();
1668 dd.values_mut()[0] = xx.values()[0];
1669 }
1670 fn eval_jac_c(&mut self, _x: &dyn Vector) -> Rc<dyn Matrix> {
1671 unimplemented!("not exercised in Phase 5 unit tests")
1672 }
1673 fn eval_jac_d(&mut self, _x: &dyn Vector) -> Rc<dyn Matrix> {
1674 unimplemented!("not exercised in Phase 5 unit tests")
1675 }
1676 fn eval_h(
1677 &mut self,
1678 _x: &dyn Vector,
1679 _obj_factor: Number,
1680 _y_c: &dyn Vector,
1681 _y_d: &dyn Vector,
1682 ) -> Rc<dyn SymMatrix> {
1683 unimplemented!()
1684 }
1685 }
1686
1687 impl IpoptNlp for MockNlp {
1688 fn x_l(&self) -> &dyn Vector {
1689 &self.x_l
1690 }
1691 fn x_u(&self) -> &dyn Vector {
1692 &self.x_u
1693 }
1694 fn d_l(&self) -> &dyn Vector {
1695 &self.d_l
1696 }
1697 fn d_u(&self) -> &dyn Vector {
1698 &self.d_u
1699 }
1700 fn px_l(&self) -> Rc<dyn Matrix> {
1701 self.px_l.clone()
1702 }
1703 fn px_u(&self) -> Rc<dyn Matrix> {
1704 self.px_u.clone()
1705 }
1706 fn pd_l(&self) -> Rc<dyn Matrix> {
1707 self.pd_l.clone()
1708 }
1709 fn pd_u(&self) -> Rc<dyn Matrix> {
1710 self.pd_u.clone()
1711 }
1712 }
1713
1714 fn fixture() -> IpoptCalculatedQuantities {
1715 let mut data = IpoptData::new();
1716 data.curr_mu = 0.1;
1717 let iv = IteratesVector::new(
1721 rcv(&[2.0, 3.0]),
1722 rcv(&[4.0]),
1723 rcv(&[1.0]),
1724 rcv(&[1.0]),
1725 rcv(&[0.5]),
1726 rcv(&[0.7]),
1727 rcv(&[0.3]),
1728 rcv(&[]),
1729 );
1730 data.set_curr(iv);
1731 let data_handle = StdRc::new(RefCell::new(data));
1732 let nlp: StdRc<RefCell<dyn IpoptNlp>> = StdRc::new(RefCell::new(MockNlp::new()));
1733 let mut cq = IpoptCalculatedQuantities::new(data_handle, nlp);
1734 cq.kappa_d = 0.0;
1736 cq
1737 }
1738
1739 fn dense_vals(v: &Rc<dyn Vector>) -> Vec<Number> {
1740 v.as_any()
1741 .downcast_ref::<DenseVector>()
1742 .unwrap()
1743 .values()
1744 .to_vec()
1745 }
1746
1747 #[test]
1748 fn slack_x_lower_is_x0_minus_x_l() {
1749 let cq = fixture();
1751 assert_eq!(dense_vals(&cq.curr_slack_x_l()), vec![2.0]);
1752 }
1753
1754 #[test]
1755 fn slack_x_upper_is_x_u_minus_x1() {
1756 let cq = fixture();
1758 assert_eq!(dense_vals(&cq.curr_slack_x_u()), vec![2.0]);
1759 }
1760
1761 #[test]
1762 fn slack_s_lower() {
1763 let cq = fixture();
1765 assert_eq!(dense_vals(&cq.curr_slack_s_l()), vec![3.0]);
1766 }
1767
1768 #[test]
1769 fn grad_f_is_twice_x() {
1770 let cq = fixture();
1771 assert_eq!(dense_vals(&cq.curr_grad_f()), vec![4.0, 6.0]);
1772 }
1773
1774 #[test]
1775 fn compl_x_l_is_slack_times_z() {
1776 let cq = fixture();
1778 assert_eq!(dense_vals(&cq.curr_compl_x_l()), vec![1.0]);
1779 }
1780
1781 #[test]
1782 fn relaxed_compl_x_l_subtracts_mu() {
1783 let cq = fixture();
1785 assert!((dense_vals(&cq.curr_relaxed_compl_x_l())[0] - 0.9).abs() < 1e-15);
1786 }
1787
1788 #[test]
1789 fn sigma_x_routes_z_over_slack_through_p() {
1790 let cq = fixture();
1794 let s = dense_vals(&cq.curr_sigma_x());
1795 assert!((s[0] - 0.25).abs() < 1e-15);
1796 assert!((s[1] - 0.35).abs() < 1e-15);
1797 }
1798
1799 #[test]
1800 fn sigma_s_lower_only() {
1801 let cq = fixture();
1803 let s = dense_vals(&cq.curr_sigma_s());
1804 assert!((s[0] - 0.1).abs() < 1e-15);
1805 }
1806
1807 #[test]
1808 fn avrg_compl_averages_over_active_bounds() {
1809 let cq = fixture();
1814 assert!((cq.curr_avrg_compl() - 1.1).abs() < 1e-15);
1815 }
1816
1817 #[test]
1818 fn complementarity_min_takes_min_over_active_pairs() {
1819 let cq = fixture();
1822 assert!((cq.curr_complementarity_min() - 0.9).abs() < 1e-15);
1823 }
1824
1825 #[test]
1826 fn centrality_measure_is_min_over_avrg() {
1827 let cq = fixture();
1829 let xi = cq.curr_centrality_measure();
1830 assert!((xi - 0.9 / 1.1).abs() < 1e-15);
1831 }
1832
1833 #[test]
1834 fn curr_f_evaluates_objective() {
1835 let cq = fixture();
1837 assert!((cq.curr_f() - 13.0).abs() < 1e-15);
1838 }
1839
1840 #[test]
1841 fn curr_barrier_obj_subtracts_mu_log_slacks() {
1842 let cq = fixture();
1846 let expected = 13.0 - 0.1 * (2.0 * 2.0_f64.ln() + 3.0_f64.ln());
1847 assert!((cq.curr_barrier_obj() - expected).abs() < 1e-13);
1848 }
1849
1850 #[test]
1851 fn curr_constraint_violation_is_one_norm() {
1852 let cq = fixture();
1856 assert!((cq.curr_constraint_violation() - 6.0).abs() < 1e-13);
1857 }
1858
1859 #[test]
1860 fn grad_barrier_obj_x_subtracts_mu_inv_slack() {
1861 let cq = fixture();
1866 let g = dense_vals(&cq.curr_grad_barrier_obj_x());
1867 assert!((g[0] - 3.95).abs() < 1e-13);
1868 assert!((g[1] - 6.05).abs() < 1e-13);
1869 }
1870
1871 #[test]
1872 fn grad_lag_s_is_minus_y_d_minus_pl_v_l_plus_pu_v_u() {
1873 let cq = fixture();
1877 assert!((dense_vals(&cq.curr_grad_lag_s())[0] + 1.3).abs() < 1e-15);
1878 }
1879
1880 fn zero_iv_like(iv: &IteratesVector) -> IteratesVector {
1881 IteratesVector::new(
1884 rcv(&vec![0.0; iv.x.dim() as usize]),
1885 rcv(&vec![0.0; iv.s.dim() as usize]),
1886 rcv(&vec![0.0; iv.y_c.dim() as usize]),
1887 rcv(&vec![0.0; iv.y_d.dim() as usize]),
1888 rcv(&vec![0.0; iv.z_l.dim() as usize]),
1889 rcv(&vec![0.0; iv.z_u.dim() as usize]),
1890 rcv(&vec![0.0; iv.v_l.dim() as usize]),
1891 rcv(&vec![0.0; iv.v_u.dim() as usize]),
1892 )
1893 }
1894
1895 #[test]
1896 fn aff_step_compl_avrg_with_zero_step_matches_curr_avrg_compl() {
1897 let cq = fixture();
1901 let iv = cq.curr_iv();
1902 let zero = zero_iv_like(&iv);
1903 let m = cq.aff_step_compl_avrg(&zero, 1.0, 1.0);
1904 assert!((m - 1.1).abs() < 1e-13);
1905 assert!((cq.curr_avrg_compl() - 1.1).abs() < 1e-13);
1906 }
1907
1908 #[test]
1909 fn aff_step_compl_avrg_responds_to_primal_step() {
1910 let cq = fixture();
1914 let iv = cq.curr_iv();
1915 let mut z = zero_iv_like(&iv);
1916 z.x = rcv(&[1.0, 0.0]);
1917 let m = cq.aff_step_compl_avrg(&z, 1.0, 1.0);
1918 assert!((m - 1.2666666666666666).abs() < 1e-13);
1919 }
1920
1921 #[test]
1922 fn aff_step_alpha_primal_truncates_to_x_lower_bound() {
1923 let cq = fixture();
1925 let iv = cq.curr_iv();
1926 let mut z = zero_iv_like(&iv);
1927 z.x = rcv(&[-3.0, 0.0]);
1928 let a = cq.aff_step_alpha_primal_max(&z, 1.0);
1929 assert!((a - 2.0 / 3.0).abs() < 1e-13);
1930 }
1931
1932 #[test]
1933 fn aff_step_alpha_dual_truncates_to_z_lower_bound() {
1934 let cq = fixture();
1936 let iv = cq.curr_iv();
1937 let mut z = zero_iv_like(&iv);
1938 z.z_l = rcv(&[-1.0]);
1939 let a = cq.aff_step_alpha_dual_max(&z, 1.0);
1940 assert!((a - 0.5).abs() < 1e-13);
1941 }
1942
1943 #[test]
1944 fn grad_barr_t_delta_dots_barrier_grads_with_step() {
1945 let cq = fixture();
1949 let dx = dvec(&[1.0, 2.0]);
1950 let ds = dvec(&[3.0]);
1951 let r = cq.curr_grad_barr_t_delta(&dx, &ds);
1952 let expected = 3.95 + 12.10 - 0.1;
1953 assert!((r - expected).abs() < 1e-13, "r = {r}");
1954 }
1955
1956 #[test]
1957 fn dwd_with_no_w_collapses_to_sigma_quadratic() {
1958 let cq = fixture();
1963 let dx = dvec(&[2.0, -1.0]);
1964 let ds = dvec(&[3.0]);
1965 let r = cq.curr_dwd(&dx, &ds);
1966 assert!((r - 2.25).abs() < 1e-13, "r = {r}");
1967 }
1968
1969 #[test]
1970 fn dwd_includes_pd_perturbations() {
1971 let cq = fixture();
1977 {
1978 let mut d = cq.data.borrow_mut();
1979 d.perturbations.delta_x = 0.5;
1980 d.perturbations.delta_s = 0.25;
1981 }
1982 let dx = dvec(&[2.0, -1.0]);
1983 let ds = dvec(&[3.0]);
1984 let r = cq.curr_dwd(&dx, &ds);
1985 assert!((r - 7.00).abs() < 1e-13, "r = {r}");
1986 }
1987}