1use crate::ipopt_cq::IpoptCqHandle;
11use crate::ipopt_data::IpoptDataHandle;
12use crate::ipopt_nlp::IpoptNlp;
13use crate::iterates_vector::{IteratesVector, IteratesVectorMut};
14use crate::kkt::aug_system_solver::{AugSysCoeffs, AugSysRhs, AugSysSol, AugSystemSolver};
15use crate::kkt::pd_system_solver::PdSystemSolver;
16use crate::kkt::perturbation_handler::PdPerturbationHandler;
17use pounce_common::tagged::Tag;
18use pounce_common::types::{Index, Number};
19use pounce_linalg::{Matrix, SymMatrix, Vector};
20use pounce_linsol::ESymSolverStatus;
21use std::cell::RefCell;
22use std::rc::Rc;
23
24pub struct PdFullSpaceSolver {
25 aug_solver: Box<dyn AugSystemSolver>,
26 perturb: Rc<RefCell<PdPerturbationHandler>>,
27 pub min_refinement_steps: Index,
28 pub max_refinement_steps: Index,
29 pub residual_ratio_max: Number,
30 pub residual_ratio_singular: Number,
31 pub residual_improvement_factor: Number,
32 pub neg_curv_test_tol: Number,
36 augsys_improved: bool,
39 matrix_considered: bool,
49 last_dep_tags: Option<[Tag; 13]>,
56 last_status: Option<ESymSolverStatus>,
57}
58
59impl PdFullSpaceSolver {
60 pub fn new(
61 aug_solver: Box<dyn AugSystemSolver>,
62 perturb: Rc<RefCell<PdPerturbationHandler>>,
63 ) -> Self {
64 Self {
65 aug_solver,
66 perturb,
67 min_refinement_steps: 1,
69 max_refinement_steps: 10,
70 residual_ratio_max: 1e-10,
71 residual_ratio_singular: 1e-5,
72 residual_improvement_factor: 0.999_999_999,
73 neg_curv_test_tol: 0.0,
74 augsys_improved: false,
75 matrix_considered: false,
76 last_dep_tags: None,
77 last_status: None,
78 }
79 }
80
81 pub fn aug_solver(&self) -> &dyn AugSystemSolver {
82 &*self.aug_solver
83 }
84
85 pub fn aug_solver_mut(&mut self) -> &mut dyn AugSystemSolver {
86 &mut *self.aug_solver
87 }
88
89 pub fn wrap_aug_solver<F>(&mut self, wrap: F)
95 where
96 F: FnOnce(Box<dyn AugSystemSolver>) -> Box<dyn AugSystemSolver>,
97 {
98 let noop: Box<dyn AugSystemSolver> = Box::new(NoopAugSolver);
102 let inner = std::mem::replace(&mut self.aug_solver, noop);
103 self.aug_solver = wrap(inner);
104 }
105
106 #[allow(clippy::too_many_arguments)]
112 pub fn solve(
113 &mut self,
114 data: &IpoptDataHandle,
115 cq: &IpoptCqHandle,
116 nlp: &Rc<RefCell<dyn IpoptNlp>>,
117 alpha: Number,
118 beta: Number,
119 rhs: &IteratesVector,
120 res: &mut IteratesVectorMut,
121 allow_inexact: bool,
122 improve_solution: bool,
123 ) -> bool {
124 debug_assert!(!allow_inexact || !improve_solution);
125 debug_assert!(!improve_solution || beta == 0.0);
126
127 let copy_res: Option<IteratesVector> = if beta != 0.0 {
130 Some(snapshot_mut(res))
131 } else {
132 None
133 };
134
135 let w = data
139 .borrow()
140 .w
141 .clone()
142 .unwrap_or_else(|| panic!("PdFullSpaceSolver::solve: IpoptData::w is unset"));
143 let cq_ref = cq.borrow();
144 let j_c = cq_ref.curr_jac_c();
145 let j_d = cq_ref.curr_jac_d();
146 let sigma_x = cq_ref.curr_sigma_x();
147 let sigma_s = cq_ref.curr_sigma_s();
148 let slack_x_l = cq_ref.curr_slack_x_l();
149 let slack_x_u = cq_ref.curr_slack_x_u();
150 let slack_s_l = cq_ref.curr_slack_s_l();
151 let slack_s_u = cq_ref.curr_slack_s_u();
152 drop(cq_ref);
153
154 let nlp_ref = nlp.borrow();
155 let px_l = nlp_ref.px_l();
156 let px_u = nlp_ref.px_u();
157 let pd_l = nlp_ref.pd_l();
158 let pd_u = nlp_ref.pd_u();
159 drop(nlp_ref);
160
161 let curr = {
162 let d = data.borrow();
163 d.curr
164 .clone()
165 .unwrap_or_else(|| panic!("PdFullSpaceSolver::solve: IpoptData::curr is unset"))
166 };
167
168 let blocks = SolveBlocks {
169 w: &*w,
170 j_c: &*j_c,
171 j_d: &*j_d,
172 px_l: &*px_l,
173 px_u: &*px_u,
174 pd_l: &*pd_l,
175 pd_u: &*pd_u,
176 z_l: &*curr.z_l,
177 z_u: &*curr.z_u,
178 v_l: &*curr.v_l,
179 v_u: &*curr.v_u,
180 slack_x_l: &*slack_x_l,
181 slack_x_u: &*slack_x_u,
182 slack_s_l: &*slack_s_l,
183 slack_s_u: &*slack_s_u,
184 sigma_x: &*sigma_x,
185 sigma_s: &*sigma_s,
186 };
187
188 let cur_tags: [Tag; 13] = [
196 blocks.w.as_tagged().get_tag(),
197 blocks.j_c.as_tagged().get_tag(),
198 blocks.j_d.as_tagged().get_tag(),
199 blocks.z_l.as_tagged().get_tag(),
200 blocks.z_u.as_tagged().get_tag(),
201 blocks.v_l.as_tagged().get_tag(),
202 blocks.v_u.as_tagged().get_tag(),
203 blocks.slack_x_l.as_tagged().get_tag(),
204 blocks.slack_x_u.as_tagged().get_tag(),
205 blocks.slack_s_l.as_tagged().get_tag(),
206 blocks.slack_s_u.as_tagged().get_tag(),
207 blocks.sigma_x.as_tagged().get_tag(),
208 blocks.sigma_s.as_tagged().get_tag(),
209 ];
210 let uptodate = self.last_dep_tags.map_or(false, |prev| prev == cur_tags);
211 if !uptodate {
212 if std::env::var_os("POUNCE_DBG_PD_TAGS").is_some() {
213 if let Some(prev) = self.last_dep_tags {
214 let names = [
215 "w",
216 "j_c",
217 "j_d",
218 "z_l",
219 "z_u",
220 "v_l",
221 "v_u",
222 "slack_x_l",
223 "slack_x_u",
224 "slack_s_l",
225 "slack_s_u",
226 "sigma_x",
227 "sigma_s",
228 ];
229 let mut diffs = String::new();
230 for i in 0..13 {
231 if prev[i] != cur_tags[i] {
232 diffs.push_str(&format!(
233 " {}({:?}→{:?})",
234 names[i], prev[i], cur_tags[i]
235 ));
236 }
237 }
238 eprintln!("[PN_PD_TAGS] cache_miss diffs:{}", diffs);
239 } else {
240 eprintln!("[PN_PD_TAGS] cache_miss first_solve");
241 }
242 }
243 self.last_dep_tags = Some(cur_tags);
244 self.matrix_considered = false;
245 self.augsys_improved = false;
246 }
247
248 let mut done = false;
249 let mut resolve_with_better_quality = false;
250 let mut pretend_singular = false;
251 let mut pretend_singular_last_time = false;
252 let mut improve = improve_solution;
253
254 while !done {
255 let solve_ok = if improve {
256 true
257 } else {
258 let ok = self.solve_once(
259 data,
260 &blocks,
261 1.0,
262 0.0,
263 rhs,
264 res,
265 resolve_with_better_quality,
266 pretend_singular,
267 );
268 resolve_with_better_quality = false;
269 pretend_singular = false;
270 ok
271 };
272 improve = false;
273
274 if !solve_ok {
275 return false;
276 }
277
278 if allow_inexact {
279 break;
280 }
281
282 let mut resid = res.fresh_zeroed();
284 self.compute_residuals(data, &blocks, rhs, res, &mut resid);
285 let mut residual_ratio = self.compute_residual_ratio(rhs, res, &resid);
286 let mut residual_ratio_old = residual_ratio;
287
288 let mut num_iter_ref: Index = 0;
289 let mut quit_refinement = false;
290
291 while !quit_refinement
292 && (num_iter_ref < self.min_refinement_steps
293 || residual_ratio > self.residual_ratio_max)
294 {
295 let frozen_resid = resid.freeze();
296 let solve_ok = self.solve_once(
297 data,
298 &blocks,
299 -1.0,
300 1.0,
301 &frozen_resid,
302 res,
303 resolve_with_better_quality,
304 false,
305 );
306 resid = thaw(frozen_resid);
307 if !solve_ok {
308 return false;
309 }
310
311 self.compute_residuals(data, &blocks, rhs, res, &mut resid);
312 residual_ratio = self.compute_residual_ratio(rhs, res, &resid);
313 num_iter_ref += 1;
314
315 if residual_ratio > self.residual_ratio_max
316 && num_iter_ref > self.min_refinement_steps
317 && (num_iter_ref > self.max_refinement_steps
318 || residual_ratio > self.residual_improvement_factor * residual_ratio_old)
319 {
320 quit_refinement = true;
321 resolve_with_better_quality = false;
322
323 if !pretend_singular_last_time {
324 if !self.augsys_improved {
325 self.augsys_improved = self.aug_solver.increase_quality();
326 if self.augsys_improved {
327 data.borrow_mut().append_info_string("q");
328 resolve_with_better_quality = true;
329 } else {
330 pretend_singular = true;
331 }
332 } else {
333 pretend_singular = true;
334 }
335 pretend_singular_last_time = pretend_singular;
336 if pretend_singular {
337 if residual_ratio < self.residual_ratio_singular {
338 pretend_singular = false;
339 data.borrow_mut().append_info_string("S");
340 } else {
341 data.borrow_mut().append_info_string("s");
342 }
343 }
344 } else {
345 pretend_singular = false;
346 }
347 }
348
349 residual_ratio_old = residual_ratio;
350 }
351
352 done = !resolve_with_better_quality && !pretend_singular;
353 }
354
355 if alpha != 0.0 {
357 res.scal(alpha);
358 }
359 if let Some(copy_res) = copy_res {
360 res.axpy(beta, ©_res);
361 }
362
363 self.last_status = Some(ESymSolverStatus::Success);
364 true
365 }
366
367 #[allow(clippy::too_many_arguments)]
372 fn solve_once(
373 &mut self,
374 data: &IpoptDataHandle,
375 b: &SolveBlocks<'_>,
376 alpha: Number,
377 beta: Number,
378 rhs: &IteratesVector,
379 res: &mut IteratesVectorMut,
380 _resolve_with_better_quality: bool,
381 mut pretend_singular: bool,
382 ) -> bool {
383 let mut aug_rhs_x = rhs.x.make_new_copy();
386 b.px_l
387 .add_m_sinv_z(1.0, b.slack_x_l, &*rhs.z_l, &mut *aug_rhs_x);
388 b.px_u
389 .add_m_sinv_z(-1.0, b.slack_x_u, &*rhs.z_u, &mut *aug_rhs_x);
390
391 let mut aug_rhs_s = rhs.s.make_new_copy();
392 b.pd_l
393 .add_m_sinv_z(1.0, b.slack_s_l, &*rhs.v_l, &mut *aug_rhs_s);
394 b.pd_u
395 .add_m_sinv_z(-1.0, b.slack_s_u, &*rhs.v_u, &mut *aug_rhs_s);
396
397 let mut sol = res.fresh_zeroed();
399
400 let num_neg_evals = rhs.y_c.dim() + rhs.y_d.dim();
402
403 let curr_mu = data.borrow().curr_mu;
404
405 if self.matrix_considered && !pretend_singular {
415 let d = self.perturb.borrow().current_perturbation();
416 let coeffs = AugSysCoeffs {
417 w: Some(b.w),
418 w_factor: 1.0,
419 d_x: Some(b.sigma_x),
420 delta_x: d.delta_x,
421 d_s: Some(b.sigma_s),
422 delta_s: d.delta_s,
423 j_c: b.j_c,
424 d_c: None,
425 delta_c: d.delta_c,
426 j_d: b.j_d,
427 d_d: None,
428 delta_d: d.delta_d,
429 };
430 let aug_rhs = AugSysRhs {
431 rhs_x: &*aug_rhs_x,
432 rhs_s: &*aug_rhs_s,
433 rhs_c: &*rhs.y_c,
434 rhs_d: &*rhs.y_d,
435 };
436 let mut aug_sol = AugSysSol {
437 sol_x: &mut *sol.x,
438 sol_s: &mut *sol.s,
439 sol_c: &mut *sol.y_c,
440 sol_d: &mut *sol.y_d,
441 };
442 let retval = self.aug_solver.resolve(&coeffs, &aug_rhs, &mut aug_sol);
447 if retval != ESymSolverStatus::Success {
448 return false;
449 }
450 {
455 let mut dm = data.borrow_mut();
456 dm.perturbations.delta_x = d.delta_x;
457 dm.perturbations.delta_s = d.delta_s;
458 dm.perturbations.delta_c = d.delta_c;
459 dm.perturbations.delta_d = d.delta_d;
460 }
461 expand_bound_multipliers(b, rhs, &mut sol);
462 let frozen_sol = sol.freeze();
463 res.add_one_vector(alpha, &frozen_sol, beta);
464 return true;
465 }
466
467 let mut deltas = self
468 .perturb
469 .borrow_mut()
470 .consider_new_system(curr_mu, Some(data));
471 let Some(mut d) = deltas.take() else {
472 return false;
473 };
474
475 let mut count = 0_i32;
476 let mut retval;
477 loop {
478 if pretend_singular {
479 retval = ESymSolverStatus::Singular;
480 pretend_singular = false;
481 } else {
482 count += 1;
483 let check_inertia = self.neg_curv_test_tol <= 0.0;
484 let coeffs = AugSysCoeffs {
485 w: Some(b.w),
486 w_factor: 1.0,
487 d_x: Some(b.sigma_x),
488 delta_x: d.delta_x,
489 d_s: Some(b.sigma_s),
490 delta_s: d.delta_s,
491 j_c: b.j_c,
492 d_c: None,
493 delta_c: d.delta_c,
494 j_d: b.j_d,
495 d_d: None,
496 delta_d: d.delta_d,
497 };
498 let aug_rhs = AugSysRhs {
499 rhs_x: &*aug_rhs_x,
500 rhs_s: &*aug_rhs_s,
501 rhs_c: &*rhs.y_c,
502 rhs_d: &*rhs.y_d,
503 };
504 let mut aug_sol = AugSysSol {
505 sol_x: &mut *sol.x,
506 sol_s: &mut *sol.s,
507 sol_c: &mut *sol.y_c,
508 sol_d: &mut *sol.y_d,
509 };
510 retval = self.aug_solver.solve(
511 &coeffs,
512 &aug_rhs,
513 &mut aug_sol,
514 check_inertia,
515 num_neg_evals,
516 );
517 }
518
519 if retval == ESymSolverStatus::FatalError {
520 return false;
521 }
522
523 if retval == ESymSolverStatus::Singular && (rhs.y_c.dim() + rhs.y_d.dim() > 0) {
524 let curr_mu = data.borrow().curr_mu;
525 let next = self
526 .perturb
527 .borrow_mut()
528 .perturb_for_singular(curr_mu, Some(data));
529 let Some(nd) = next else { return false };
530 d = nd;
531 } else if retval == ESymSolverStatus::WrongInertia
532 && self.aug_solver.number_of_neg_evals() < num_neg_evals
533 {
534 let mut assume_singular = true;
535 if !self.augsys_improved {
536 self.augsys_improved = self.aug_solver.increase_quality();
537 if self.augsys_improved {
538 data.borrow_mut().append_info_string("q");
539 assume_singular = false;
540 }
541 }
542 if assume_singular {
543 let curr_mu = data.borrow().curr_mu;
544 let next = self
545 .perturb
546 .borrow_mut()
547 .perturb_for_singular(curr_mu, Some(data));
548 let Some(nd) = next else { return false };
549 d = nd;
550 data.borrow_mut().append_info_string("a");
551 }
552 } else if retval == ESymSolverStatus::WrongInertia
553 || retval == ESymSolverStatus::Singular
554 {
555 let curr_mu = data.borrow().curr_mu;
556 let next = self
557 .perturb
558 .borrow_mut()
559 .perturb_for_wrong_inertia(curr_mu, Some(data));
560 let Some(nd) = next else { return false };
561 d = nd;
562 }
563
564 if retval == ESymSolverStatus::Success {
565 break;
566 }
567 }
568 let _ = count;
569
570 {
573 let mut dm = data.borrow_mut();
574 dm.perturbations.delta_x = d.delta_x;
575 dm.perturbations.delta_s = d.delta_s;
576 dm.perturbations.delta_c = d.delta_c;
577 dm.perturbations.delta_d = d.delta_d;
578 }
579
580 self.matrix_considered = true;
584
585 expand_bound_multipliers(b, rhs, &mut sol);
586
587 let frozen_sol = sol.freeze();
589 res.add_one_vector(alpha, &frozen_sol, beta);
590 true
591 }
592
593 fn compute_residuals(
596 &self,
597 _data: &IpoptDataHandle,
598 b: &SolveBlocks<'_>,
599 rhs: &IteratesVector,
600 res: &IteratesVectorMut,
601 resid: &mut IteratesVectorMut,
602 ) {
603 let d = self.perturb.borrow().current_perturbation();
604
605 b.w.mult_vector(1.0, &*res.x, 0.0, &mut *resid.x);
608 b.j_c.trans_mult_vector(1.0, &*res.y_c, 1.0, &mut *resid.x);
609 b.j_d.trans_mult_vector(1.0, &*res.y_d, 1.0, &mut *resid.x);
610 b.px_l.mult_vector(-1.0, &*res.z_l, 1.0, &mut *resid.x);
611 b.px_u.mult_vector(1.0, &*res.z_u, 1.0, &mut *resid.x);
612 resid
614 .x
615 .add_two_vectors(d.delta_x, &*res.x, -1.0, &*rhs.x, 1.0);
616
617 b.pd_u.mult_vector(1.0, &*res.v_u, 0.0, &mut *resid.s);
619 b.pd_l.mult_vector(-1.0, &*res.v_l, 1.0, &mut *resid.s);
620 resid.s.add_two_vectors(-1.0, &*res.y_d, -1.0, &*rhs.s, 1.0);
621 if d.delta_s != 0.0 {
622 resid.s.axpy(d.delta_s, &*res.s);
623 }
624
625 b.j_c.mult_vector(1.0, &*res.x, 0.0, &mut *resid.y_c);
627 resid
628 .y_c
629 .add_two_vectors(-d.delta_c, &*res.y_c, -1.0, &*rhs.y_c, 1.0);
630
631 b.j_d.mult_vector(1.0, &*res.x, 0.0, &mut *resid.y_d);
633 resid
634 .y_d
635 .add_two_vectors(-1.0, &*res.s, -1.0, &*rhs.y_d, 1.0);
636 if d.delta_d != 0.0 {
637 resid.y_d.axpy(-d.delta_d, &*res.y_d);
638 }
639
640 resid.z_l.copy(&*res.z_l);
642 resid.z_l.element_wise_multiply(b.slack_x_l);
643 let mut tmp_zl = b.z_l.make_new();
644 b.px_l.trans_mult_vector(1.0, &*res.x, 0.0, &mut *tmp_zl);
645 tmp_zl.element_wise_multiply(b.z_l);
646 resid
647 .z_l
648 .add_two_vectors(1.0, &*tmp_zl, -1.0, &*rhs.z_l, 1.0);
649
650 resid.z_u.copy(&*res.z_u);
652 resid.z_u.element_wise_multiply(b.slack_x_u);
653 let mut tmp_zu = b.z_u.make_new();
654 b.px_u.trans_mult_vector(1.0, &*res.x, 0.0, &mut *tmp_zu);
655 tmp_zu.element_wise_multiply(b.z_u);
656 resid
657 .z_u
658 .add_two_vectors(-1.0, &*tmp_zu, -1.0, &*rhs.z_u, 1.0);
659
660 resid.v_l.copy(&*res.v_l);
662 resid.v_l.element_wise_multiply(b.slack_s_l);
663 let mut tmp_vl = b.v_l.make_new();
664 b.pd_l.trans_mult_vector(1.0, &*res.s, 0.0, &mut *tmp_vl);
665 tmp_vl.element_wise_multiply(b.v_l);
666 resid
667 .v_l
668 .add_two_vectors(1.0, &*tmp_vl, -1.0, &*rhs.v_l, 1.0);
669
670 resid.v_u.copy(&*res.v_u);
672 resid.v_u.element_wise_multiply(b.slack_s_u);
673 let mut tmp_vu = b.v_u.make_new();
674 b.pd_u.trans_mult_vector(1.0, &*res.s, 0.0, &mut *tmp_vu);
675 tmp_vu.element_wise_multiply(b.v_u);
676 resid
677 .v_u
678 .add_two_vectors(-1.0, &*tmp_vu, -1.0, &*rhs.v_u, 1.0);
679 }
680
681 fn compute_residual_ratio(
684 &self,
685 rhs: &IteratesVector,
686 res: &IteratesVectorMut,
687 resid: &IteratesVectorMut,
688 ) -> Number {
689 let nrm_rhs = rhs.amax();
690 let nrm_res = res.amax();
691 let nrm_resid = resid.amax();
692 if nrm_rhs + nrm_res == 0.0 {
693 nrm_resid
694 } else {
695 let max_cond = 1e6;
696 nrm_resid / (nrm_res.min(max_cond * nrm_rhs) + nrm_rhs)
697 }
698 }
699}
700
701impl PdSystemSolver for PdFullSpaceSolver {
702 fn solve_status(&self) -> ESymSolverStatus {
703 self.last_status.unwrap_or(ESymSolverStatus::FatalError)
704 }
705}
706
707struct SolveBlocks<'a> {
710 w: &'a dyn SymMatrix,
711 j_c: &'a dyn Matrix,
712 j_d: &'a dyn Matrix,
713 px_l: &'a dyn Matrix,
714 px_u: &'a dyn Matrix,
715 pd_l: &'a dyn Matrix,
716 pd_u: &'a dyn Matrix,
717 z_l: &'a dyn Vector,
718 z_u: &'a dyn Vector,
719 v_l: &'a dyn Vector,
720 v_u: &'a dyn Vector,
721 slack_x_l: &'a dyn Vector,
722 slack_x_u: &'a dyn Vector,
723 slack_s_l: &'a dyn Vector,
724 slack_s_u: &'a dyn Vector,
725 sigma_x: &'a dyn Vector,
726 sigma_s: &'a dyn Vector,
727}
728
729trait FreshZeroed {
733 fn fresh_zeroed(&self) -> IteratesVectorMut;
734}
735
736impl FreshZeroed for IteratesVectorMut {
737 fn fresh_zeroed(&self) -> IteratesVectorMut {
738 IteratesVectorMut {
739 x: self.x.make_new(),
740 s: self.s.make_new(),
741 y_c: self.y_c.make_new(),
742 y_d: self.y_d.make_new(),
743 z_l: self.z_l.make_new(),
744 z_u: self.z_u.make_new(),
745 v_l: self.v_l.make_new(),
746 v_u: self.v_u.make_new(),
747 }
748 }
749}
750
751fn snapshot_mut(m: &IteratesVectorMut) -> IteratesVector {
754 let mut out = m.fresh_zeroed();
755 out.x.copy(&*m.x);
756 out.s.copy(&*m.s);
757 out.y_c.copy(&*m.y_c);
758 out.y_d.copy(&*m.y_d);
759 out.z_l.copy(&*m.z_l);
760 out.z_u.copy(&*m.z_u);
761 out.v_l.copy(&*m.v_l);
762 out.v_u.copy(&*m.v_u);
763 out.freeze()
764}
765
766fn expand_bound_multipliers(
784 b: &SolveBlocks<'_>,
785 rhs: &IteratesVector,
786 sol: &mut IteratesVectorMut,
787) {
788 b.px_l
789 .sinv_blrm_zmt_dbr(-1.0, b.slack_x_l, &*rhs.z_l, b.z_l, &*sol.x, &mut *sol.z_l);
790 b.px_u
791 .sinv_blrm_zmt_dbr(1.0, b.slack_x_u, &*rhs.z_u, b.z_u, &*sol.x, &mut *sol.z_u);
792 b.pd_l
793 .sinv_blrm_zmt_dbr(-1.0, b.slack_s_l, &*rhs.v_l, b.v_l, &*sol.s, &mut *sol.v_l);
794 b.pd_u
795 .sinv_blrm_zmt_dbr(1.0, b.slack_s_u, &*rhs.v_u, b.v_u, &*sol.s, &mut *sol.v_u);
796}
797
798fn thaw(iv: IteratesVector) -> IteratesVectorMut {
799 fn one(v: Rc<dyn Vector>) -> Box<dyn Vector> {
800 let mut b = v.make_new();
801 b.copy(&*v);
802 b
803 }
804 IteratesVectorMut {
805 x: one(iv.x),
806 s: one(iv.s),
807 y_c: one(iv.y_c),
808 y_d: one(iv.y_d),
809 z_l: one(iv.z_l),
810 z_u: one(iv.z_u),
811 v_l: one(iv.v_l),
812 v_u: one(iv.v_u),
813 }
814}
815
816struct NoopAugSolver;
821
822impl AugSystemSolver for NoopAugSolver {
823 fn provides_inertia(&self) -> bool {
824 unreachable!("NoopAugSolver is a transient placeholder")
825 }
826 fn number_of_neg_evals(&self) -> Index {
827 unreachable!("NoopAugSolver is a transient placeholder")
828 }
829 fn increase_quality(&mut self) -> bool {
830 unreachable!("NoopAugSolver is a transient placeholder")
831 }
832 fn last_solve_status(&self) -> ESymSolverStatus {
833 unreachable!("NoopAugSolver is a transient placeholder")
834 }
835 fn solve(
836 &mut self,
837 _coeffs: &AugSysCoeffs<'_>,
838 _rhs: &AugSysRhs<'_>,
839 _sol: &mut AugSysSol<'_>,
840 _check_neg_evals: bool,
841 _num_neg_evals: Index,
842 ) -> ESymSolverStatus {
843 unreachable!("NoopAugSolver is a transient placeholder")
844 }
845}