1use crate::eq_mult::r#trait::EqMultCalculator;
30use crate::init::r#trait::IterateInitializer;
31use crate::ipopt_cq::IpoptCqHandle;
32use crate::ipopt_data::IpoptDataHandle;
33use crate::ipopt_nlp::IpoptNlp;
34use crate::iterates_vector::IteratesVector;
35use crate::kkt::aug_system_solver::AugSystemSolver;
36use pounce_common::types::Number;
37use pounce_linalg::dense_vector::{DenseVector, DenseVectorSpace};
38use pounce_linalg::Vector;
39use std::cell::RefCell;
40use std::rc::Rc;
41
42pub struct DefaultIterateInitializer {
43 pub bound_push: Number,
44 pub bound_frac: Number,
45 pub slack_bound_push: Number,
46 pub slack_bound_frac: Number,
47 pub constr_mult_init_max: Number,
48 pub bound_mult_init_val: Number,
49 pub bound_mult_init_method: String,
51 pub eq_mult_calculator: Option<Box<dyn EqMultCalculator>>,
56}
57
58impl Default for DefaultIterateInitializer {
59 fn default() -> Self {
60 Self {
61 bound_push: 1e-2,
62 bound_frac: 1e-2,
63 slack_bound_push: 1e-2,
64 slack_bound_frac: 1e-2,
65 constr_mult_init_max: 1e3,
66 bound_mult_init_val: 1.0,
67 bound_mult_init_method: "constant".into(),
68 eq_mult_calculator: None,
69 }
70 }
71}
72
73impl DefaultIterateInitializer {
74 pub fn new() -> Self {
75 Self::default()
76 }
77
78 pub fn with_eq_mult_calculator(eq_mult: Box<dyn EqMultCalculator>) -> Self {
79 Self {
80 eq_mult_calculator: Some(eq_mult),
81 ..Self::default()
82 }
83 }
84
85 pub fn push_to_interior(
101 bound_push: Number,
102 bound_frac: Number,
103 x: Number,
104 lower: Option<Number>,
105 upper: Option<Number>,
106 ) -> Number {
107 match (lower, upper) {
108 (Some(lo), Some(hi)) => {
109 let span = hi - lo;
110 let p_l = (bound_push * lo.abs().max(1.0)).min(bound_frac * span);
111 let p_u = (bound_push * hi.abs().max(1.0)).min(bound_frac * span);
112 x.max(lo + p_l).min(hi - p_u)
113 }
114 (Some(lo), None) => {
115 let p_l = bound_push * lo.abs().max(1.0);
116 x.max(lo + p_l)
117 }
118 (None, Some(hi)) => {
119 let p_u = bound_push * hi.abs().max(1.0);
120 x.min(hi - p_u)
121 }
122 (None, None) => x,
123 }
124 }
125}
126
127impl IterateInitializer for DefaultIterateInitializer {
128 fn set_initial_iterates(
129 &mut self,
130 data: &IpoptDataHandle,
131 cq: &IpoptCqHandle,
132 nlp: &Rc<RefCell<dyn IpoptNlp>>,
133 aug_solver: &mut dyn AugSystemSolver,
134 ) -> bool {
135 let curr_template = match data.borrow().curr.clone() {
136 Some(c) => c,
137 None => return false,
138 };
139
140 let n_x = curr_template.x.dim();
141 let n_s = curr_template.s.dim();
142 let n_yc = curr_template.y_c.dim();
143 let n_yd = curr_template.y_d.dim();
144 let n_zl = curr_template.z_l.dim();
145 let n_zu = curr_template.z_u.dim();
146 let n_vl = curr_template.v_l.dim();
147 let n_vu = curr_template.v_u.dim();
148
149 let mut x = DenseVectorSpace::new(n_x).make_new_dense();
154 nlp.borrow_mut().get_starting_x(&mut x);
155 {
156 let nlp_ref = nlp.borrow();
157 push_x_into_interior(
158 &mut x,
159 &*nlp_ref.px_l(),
160 nlp_ref.x_l(),
161 &*nlp_ref.px_u(),
162 nlp_ref.x_u(),
163 self.bound_push,
164 self.bound_frac,
165 );
166 }
167
168 let mut s = DenseVectorSpace::new(n_s).make_new_dense();
170 nlp.borrow_mut().eval_d(&x, &mut s);
171 {
172 let nlp_ref = nlp.borrow();
173 push_x_into_interior(
174 &mut s,
175 &*nlp_ref.pd_l(),
176 nlp_ref.d_l(),
177 &*nlp_ref.pd_u(),
178 nlp_ref.d_u(),
179 self.slack_bound_push,
180 self.slack_bound_frac,
181 );
182 }
183
184 let mut y_c = DenseVectorSpace::new(n_yc).make_new_dense();
187 let mut y_d = DenseVectorSpace::new(n_yd).make_new_dense();
188 if self.bound_mult_init_method == "constant" {
189 y_c.set(0.0);
192 y_d.set(0.0);
193 } else {
194 nlp.borrow_mut().get_starting_y(&mut y_c, &mut y_d);
197 cap_constraint_multipliers(&mut y_c, self.constr_mult_init_max);
198 cap_constraint_multipliers(&mut y_d, self.constr_mult_init_max);
199 }
200
201 let mut z_l = DenseVectorSpace::new(n_zl).make_new_dense();
203 let mut z_u = DenseVectorSpace::new(n_zu).make_new_dense();
204 let mut v_l = DenseVectorSpace::new(n_vl).make_new_dense();
205 let mut v_u = DenseVectorSpace::new(n_vu).make_new_dense();
206 z_l.set(self.bound_mult_init_val);
207 z_u.set(self.bound_mult_init_val);
208 v_l.set(self.bound_mult_init_val);
209 v_u.set(self.bound_mult_init_val);
210
211 let iv = IteratesVector::new(
212 Rc::new(x),
213 Rc::new(s),
214 Rc::new(y_c),
215 Rc::new(y_d),
216 Rc::new(z_l),
217 Rc::new(z_u),
218 Rc::new(v_l),
219 Rc::new(v_u),
220 );
221 let n_x_dim = iv.x.dim();
222 data.borrow_mut().set_curr(iv);
223
224 if n_yc != n_x_dim
232 && self.constr_mult_init_max > 0.0
233 && (n_yc + n_yd) > 0
234 && self.eq_mult_calculator.is_some()
235 {
236 let mut new_y_c = DenseVectorSpace::new(n_yc).make_new_dense();
237 let mut new_y_d = DenseVectorSpace::new(n_yd).make_new_dense();
238 let calc = self.eq_mult_calculator.as_mut().unwrap();
239 let ok = calc.calculate_y_eq(data, cq, nlp, aug_solver, &mut new_y_c, &mut new_y_d);
240 if !ok {
241 data.borrow_mut().append_info_string("y0");
243 } else {
244 let yinitnrm = new_y_c.amax().max(new_y_d.amax());
245 if yinitnrm > self.constr_mult_init_max {
246 data.borrow_mut().append_info_string("yc");
249 } else {
250 let curr = data.borrow().curr.clone();
254 if let Some(c) = curr {
255 let new_iv = IteratesVector::new(
256 c.x.clone(),
257 c.s.clone(),
258 Rc::new(new_y_c),
259 Rc::new(new_y_d),
260 c.z_l.clone(),
261 c.z_u.clone(),
262 c.v_l.clone(),
263 c.v_u.clone(),
264 );
265 let mut d = data.borrow_mut();
266 d.set_curr(new_iv);
267 d.append_info_string("y");
268 }
269 }
270 }
271 }
272
273 true
274 }
275}
276
277pub(crate) fn push_x_into_interior(
283 x: &mut DenseVector,
284 px_l: &dyn pounce_linalg::Matrix,
285 x_l: &dyn Vector,
286 px_u: &dyn pounce_linalg::Matrix,
287 x_u: &dyn Vector,
288 bound_push: Number,
289 bound_frac: Number,
290) {
291 let n = x.dim() as usize;
298 let mut lower = vec![None; n];
302 let mut upper = vec![None; n];
303 expand_packed_into_dense(px_l, x_l, &mut lower);
304 expand_packed_into_dense(px_u, x_u, &mut upper);
305
306 let xs = x.values_mut();
307 for (i, xi) in xs.iter_mut().enumerate() {
308 *xi = DefaultIterateInitializer::push_to_interior(
309 bound_push, bound_frac, *xi, lower[i], upper[i],
310 );
311 }
312}
313
314fn expand_packed_into_dense(
320 p: &dyn pounce_linalg::Matrix,
321 b_packed: &dyn Vector,
322 out: &mut [Option<Number>],
323) {
324 use pounce_linalg::expansion_matrix::ExpansionMatrix;
325 let dim_packed = b_packed.dim() as usize;
326 if dim_packed == 0 {
327 return;
328 }
329
330 if let Some(em) = p.as_any().downcast_ref::<ExpansionMatrix>() {
331 let rows = em.expanded_pos_indices();
332 let Some(packed) = b_packed.as_any().downcast_ref::<DenseVector>() else {
333 unreachable!("expansion-matrix bound vec must be DenseVector")
334 };
335 let vals = packed.values();
336 for k in 0..dim_packed {
337 let row = rows[k] as usize;
338 out[row] = Some(vals[k]);
339 }
340 } else {
341 let n_full = out.len() as i32;
344 let mut tmp = DenseVectorSpace::new(n_full).make_new_dense();
345 for k in 0..dim_packed {
346 let mut e_k = DenseVectorSpace::new(b_packed.dim()).make_new_dense();
347 e_k.values_mut()[k] = 1.0;
348 tmp.set(0.0);
349 p.mult_vector(1.0, &e_k, 0.0, &mut tmp);
350 let Some(packed) = b_packed.as_any().downcast_ref::<DenseVector>() else {
354 unreachable!("packed bound vec must be DenseVector")
355 };
356 for (i, &t) in tmp.values().iter().enumerate() {
357 if t == 1.0 {
358 out[i] = Some(packed.values()[k]);
359 }
360 }
361 }
362 }
363}
364
365fn cap_constraint_multipliers(y: &mut DenseVector, max: Number) {
368 for v in y.values_mut() {
369 if *v > max {
370 *v = max;
371 } else if *v < -max {
372 *v = -max;
373 }
374 }
375}
376
377#[cfg(test)]
378mod tests {
379 use super::*;
380
381 #[test]
382 fn interior_point_left_alone() {
383 let v = DefaultIterateInitializer::push_to_interior(1e-2, 1e-2, 5.0, Some(0.0), Some(10.0));
387 assert!((v - 5.0).abs() < 1e-15);
388 }
389
390 #[test]
391 fn point_at_lower_bound_pushed_in() {
392 let v = DefaultIterateInitializer::push_to_interior(1e-2, 1e-2, 0.0, Some(0.0), Some(10.0));
394 assert!((v - 0.01).abs() < 1e-15);
395 }
396
397 #[test]
398 fn point_at_upper_bound_pushed_in() {
399 let v =
401 DefaultIterateInitializer::push_to_interior(1e-2, 1e-2, 10.0, Some(0.0), Some(10.0));
402 assert!((v - 9.9).abs() < 1e-15);
403 }
404
405 #[test]
406 fn point_below_lower_bound_clamped() {
407 let v =
409 DefaultIterateInitializer::push_to_interior(1e-2, 1e-2, -5.0, Some(0.0), Some(10.0));
410 assert!((v - 0.01).abs() < 1e-15);
411 }
412
413 #[test]
414 fn lower_only_pushed_by_max_abs() {
415 let v = DefaultIterateInitializer::push_to_interior(1e-2, 1e-2, -100.0, Some(-100.0), None);
418 assert!((v - -99.0).abs() < 1e-13);
419 }
420
421 #[test]
422 fn upper_only_pushed_by_max_abs() {
423 let v = DefaultIterateInitializer::push_to_interior(1e-2, 1e-2, 50.0, None, Some(50.0));
425 assert!((v - 49.5).abs() < 1e-13);
426 }
427
428 #[test]
429 fn free_variable_unchanged() {
430 let v = DefaultIterateInitializer::push_to_interior(1e-2, 1e-2, 42.0, None, None);
431 assert_eq!(v, 42.0);
432 }
433
434 #[test]
435 fn narrow_interval_uses_bound_frac_branch() {
436 let v = DefaultIterateInitializer::push_to_interior(1e-2, 1e-2, 0.0, Some(0.0), Some(1e-4));
439 assert!((v - 1e-6).abs() < 1e-18);
440 }
441}