1use std::rc::Rc;
9
10use pounce_algorithm::ipopt_data::IpoptDataHandle;
11use pounce_algorithm::iterates_vector::IteratesVector;
12use pounce_common::types::{Index, Number};
13use pounce_linalg::Matrix;
14use pounce_linalg::expansion_matrix::ExpansionMatrix;
15use pounce_sens_core::barrier::{
16 ActivityData, BarrierIterate, BoundSide, EngineScaling, KktOperator, ModelMaps,
17};
18
19use crate::algorithm_backsolver::{CorrectorSigma, PdSensBacksolver};
20use crate::backsolver::SensBacksolver;
21use crate::vec_util::dense_to_vec;
22
23impl BarrierIterate for PdSensBacksolver {
24 fn block_dims(&self) -> [usize; 8] {
25 PdSensBacksolver::block_dims(self)
26 }
27
28 fn offsets(&self) -> [usize; 9] {
29 self.offsets_public()
30 }
31
32 fn barrier_mu(&self) -> Number {
33 PdSensBacksolver::barrier_mu(self)
34 }
35
36 fn converged_iterate(&self, out: &mut [Number]) -> Result<(), String> {
37 self.curr_flat(out)
38 .map_err(|_| "the solve holds no current iterate of that length".to_string())
39 }
40
41 fn scaled_rhs_factor(&self) -> Option<&[Number]> {
42 PdSensBacksolver::scaled_rhs_factor(self)
43 }
44
45 fn barrier_residual(&self, point: &[Number], out: &mut [Number]) -> Result<(), String> {
50 let dim = self.dim();
51 if point.len() != dim || out.len() != dim {
52 return Err(format!(
53 "barrier residual: point of length {} and out of length {} for a KKT system of {dim}",
54 point.len(),
55 out.len()
56 ));
57 }
58 let iv = self
59 .pack_public(point)
60 .map_err(|_| "corrector: pack failed".to_string())?;
61 let (data, cq, _) = self.activity_handles();
62 data.borrow_mut().set_trial(iv.freeze());
63
64 let off = self.offsets_public();
65 let cqb = cq.borrow();
66 let blocks: [Rc<dyn pounce_linalg::Vector>; 8] = [
67 cqb.trial_grad_lag_x(),
68 cqb.trial_grad_lag_s(),
69 cqb.trial_c(),
70 cqb.trial_d_minus_s(),
71 cqb.trial_compl_x_l(),
72 cqb.trial_compl_x_u(),
73 cqb.trial_compl_s_l(),
74 cqb.trial_compl_s_u(),
75 ];
76 for (i, v) in blocks.into_iter().enumerate() {
77 let vals = dense_to_vec(&*v);
78 let (a, b) = (off[i], off[i + 1]);
79 if vals.len() != b - a {
80 return Err(format!(
81 "corrector: block {i} is {} long, expected {}",
82 vals.len(),
83 b - a
84 ));
85 }
86 out[a..b].copy_from_slice(&vals);
87 }
88 Ok(())
89 }
90
91 fn corrector_operator<'a>(
105 &'a self,
106 predicted: &[Number],
107 pinned: &[(usize, Number)],
108 exact_hessian: bool,
109 ) -> Result<Box<dyn KktOperator + 'a>, String> {
110 let (data_h, cq_h, _) = self.activity_handles();
111 let predicted = self
112 .pack_natural(predicted)
113 .ok_or_else(|| "corrector: packing the predicted point failed".to_string())?;
114 let mut restore = RestoreCurr {
115 data: data_h,
116 saved: data_h.borrow().curr.clone(),
117 saved_w: None,
118 restore_w: false,
119 };
120 data_h.borrow_mut().set_curr(predicted);
121 if exact_hessian {
122 restore.saved_w = data_h.borrow().w.clone();
123 restore.restore_w = true;
124 let w = cq_h.borrow().curr_exact_hessian();
125 data_h.borrow_mut().w = Some(w);
126 }
127 let op = self
128 .corrector_sigma(pinned)
129 .ok_or_else(|| "corrector: operator diagonal unavailable".to_string())?;
130 Ok(Box::new(PdCorrectorOperator {
131 bs: self,
132 op,
133 _restore: restore,
134 }))
135 }
136
137 fn activity_data(&self) -> Result<ActivityData, String> {
138 let (data, cq, nlp) = self.activity_handles();
139
140 let (z_l, z_u, v_l, v_u) = {
144 let d = data.borrow();
145 let curr = d
146 .curr
147 .as_ref()
148 .ok_or("activity: the converged state has no iterate")?;
149 (
150 dense_to_vec(curr.z_l.as_ref()),
151 dense_to_vec(curr.z_u.as_ref()),
152 dense_to_vec(curr.v_l.as_ref()),
153 dense_to_vec(curr.v_u.as_ref()),
154 )
155 };
156 let (px_l, px_u, pd_l, pd_u) = {
157 let nl = nlp.borrow();
158 (
159 positions(&nl.px_l())?,
160 positions(&nl.px_u())?,
161 positions(&nl.pd_l())?,
162 positions(&nl.pd_u())?,
163 )
164 };
165 let (s_x_l, s_x_u, s_d_l, s_d_u, hessian, jac_c, jac_d) = {
166 let c = cq.borrow();
167 (
168 dense_to_vec(c.curr_slack_x_l().as_ref()),
169 dense_to_vec(c.curr_slack_x_u().as_ref()),
170 dense_to_vec(c.curr_slack_s_l().as_ref()),
171 dense_to_vec(c.curr_slack_s_u().as_ref()),
172 c.curr_exact_hessian(),
173 c.curr_jac_c(),
174 c.curr_jac_d(),
175 )
176 };
177 let sigma_x = dense_to_vec(self.barrier_sigma_x().as_ref());
178 let sigma_s = dense_to_vec(self.barrier_sigma_s().as_ref());
179
180 let side =
181 |pos: Vec<usize>, mult: Vec<Number>, slack: Vec<Number>| BoundSide { pos, mult, slack };
182 Ok(ActivityData {
183 x_l: side(px_l, z_l, s_x_l),
184 x_u: side(px_u, z_u, s_x_u),
185 d_l: side(pd_l, v_l, s_d_l),
186 d_u: side(pd_u, v_u, s_d_u),
187 sigma_x,
188 sigma_s,
189 hessian,
190 jac_c,
191 jac_d,
192 })
193 }
194
195 fn model_maps(&self) -> Result<ModelMaps, String> {
200 let (_, _, nlp) = self.activity_handles();
201 let nl = nlp.borrow();
202 let n_x = self.block_dims()[0];
203 let n_full_g = nl.n_full_g();
204 let block = |p: Option<Index>| p.map(|p| p as usize);
205 Ok(ModelMaps {
206 n_full_x: nl.n_full_x() as usize,
207 var_x_to_full_x: (0..n_x)
208 .map(|i| nl.var_x_to_full_x(i as Index) as usize)
209 .collect(),
210 full_g_to_c: (0..n_full_g)
211 .map(|g| block(nl.full_g_to_c_block(g)))
212 .collect(),
213 full_g_to_d: (0..n_full_g)
214 .map(|g| block(nl.full_g_to_d_block(g)))
215 .collect(),
216 })
217 }
218
219 fn scaling(&self) -> EngineScaling {
220 let (obj, c_scale, d_scale) = self.nlp_scaling();
221 EngineScaling {
222 objective: obj,
223 variables: self.variable_scaling().map(|v| v.to_vec()),
224 equality_rows: c_scale,
225 inequality_rows: d_scale,
226 }
227 }
228
229 fn primal_bounds(&self) -> Result<(Vec<Number>, Vec<Number>), String> {
232 let (_, _, nlp) = self.activity_handles();
233 let nl = nlp.borrow();
234 let [n_x, n_s, ..] = self.block_dims();
235 let (mut lo, mut hi) = pounce_sens_core::boundcheck::expand_bounds(
236 n_x,
237 &nl.px_l(),
238 &nl.px_u(),
239 nl.x_l(),
240 nl.x_u(),
241 );
242 let (s_lo, s_hi) = pounce_sens_core::boundcheck::expand_bounds(
243 n_s,
244 &nl.pd_l(),
245 &nl.pd_u(),
246 nl.d_l(),
247 nl.d_u(),
248 );
249 lo.extend_from_slice(&s_lo);
250 hi.extend_from_slice(&s_hi);
251 Ok((lo, hi))
252 }
253
254 fn kkt_perturbations(&self) -> Option<[Number; 4]> {
255 Some(PdSensBacksolver::kkt_perturbations(self))
256 }
257
258 fn released_operator<'a>(
262 &'a self,
263 released: &[usize],
264 ) -> Result<Box<dyn KktOperator + 'a>, String> {
265 let (sigma, sigma_s) = self
266 .released_sigmas(released)
267 .ok_or_else(|| "released sigma unavailable".to_string())?;
268 Ok(Box::new(PdReleasedOperator {
269 bs: self,
270 released: released.to_vec(),
271 sigma,
272 sigma_s,
273 }))
274 }
275}
276
277struct PdReleasedOperator<'a> {
280 bs: &'a PdSensBacksolver,
281 released: Vec<usize>,
282 sigma: Rc<dyn pounce_linalg::Vector>,
283 sigma_s: Option<Rc<dyn pounce_linalg::Vector>>,
284}
285
286impl KktOperator for PdReleasedOperator<'_> {
287 fn solve(&self, rhs: &[Number], lhs: &mut [Number]) -> bool {
290 self.bs.solve_released_prebuilt(
291 &self.released,
292 Rc::clone(&self.sigma),
293 self.sigma_s.as_ref().map(Rc::clone),
294 None,
295 rhs,
296 lhs,
297 false,
298 )
299 }
300}
301
302fn positions(m: &Rc<dyn Matrix>) -> Result<Vec<usize>, String> {
304 m.as_any()
305 .downcast_ref::<ExpansionMatrix>()
306 .map(|e| {
307 e.expanded_pos_indices()
308 .iter()
309 .map(|&i| i as usize)
310 .collect()
311 })
312 .ok_or_else(|| "activity: a bound projection is not an expansion matrix".to_string())
313}
314
315struct PdCorrectorOperator<'a> {
319 bs: &'a PdSensBacksolver,
320 op: CorrectorSigma,
321 _restore: RestoreCurr<'a>,
322}
323
324impl KktOperator for PdCorrectorOperator<'_> {
325 fn solve(&self, rhs: &[Number], lhs: &mut [Number]) -> bool {
338 self.bs.solve_released_prebuilt(
339 &[],
340 Rc::clone(&self.op.sigma_x),
341 Some(Rc::clone(&self.op.sigma_s)),
342 Some((&self.op.ratio_x, &self.op.ratio_s)),
343 rhs,
344 lhs,
345 false,
346 )
347 }
348}
349
350struct RestoreCurr<'a> {
353 data: &'a IpoptDataHandle,
354 saved: Option<IteratesVector>,
355 saved_w: Option<Rc<dyn pounce_linalg::SymMatrix>>,
356 restore_w: bool,
357}
358
359impl Drop for RestoreCurr<'_> {
360 fn drop(&mut self) {
361 if let Some(c) = self.saved.take() {
362 self.data.borrow_mut().set_curr(c);
363 }
364 if self.restore_w {
365 self.data.borrow_mut().w = self.saved_w.take();
366 }
367 }
368}