1use crate::alg_builder::WarmStartOptions;
31use crate::init::default::push_x_into_interior;
32use crate::init::r#trait::IterateInitializer;
33use crate::ipopt_cq::IpoptCqHandle;
34use crate::ipopt_data::IpoptDataHandle;
35use crate::ipopt_nlp::IpoptNlp;
36use crate::iterates_vector::IteratesVector;
37use crate::kkt::aug_system_solver::AugSystemSolver;
38use pounce_linalg::Vector;
39use pounce_linalg::compound_vector::CompoundVector;
40use pounce_linalg::dense_vector::{DenseVector, DenseVectorSpace};
41use std::cell::RefCell;
42use std::rc::Rc;
43
44pub struct WarmStartIterateInitializer {
45 opts: WarmStartOptions,
46}
47
48impl WarmStartIterateInitializer {
49 pub fn new() -> Self {
50 Self {
51 opts: WarmStartOptions::default(),
52 }
53 }
54
55 pub fn with_options(opts: WarmStartOptions) -> Self {
56 Self { opts }
57 }
58}
59
60impl Default for WarmStartIterateInitializer {
61 fn default() -> Self {
62 Self::new()
63 }
64}
65
66impl IterateInitializer for WarmStartIterateInitializer {
67 fn set_initial_iterates(
68 &mut self,
69 data: &IpoptDataHandle,
70 _cq: &IpoptCqHandle,
71 nlp: &Rc<RefCell<dyn IpoptNlp>>,
72 _aug_solver: &mut dyn AugSystemSolver,
73 ) -> bool {
74 let needs_seed_from_nlp = {
81 let borrow = data.borrow();
82 match borrow.curr.as_ref() {
83 None => return false,
84 Some(c) => !is_initialized(&c.x),
85 }
86 };
87
88 if needs_seed_from_nlp && !seed_from_nlp(data, nlp, &self.opts) {
89 return false;
90 }
91
92 {
93 let mut borrow = data.borrow_mut();
111 let curr = borrow.curr.as_ref().unwrap();
112 let cap = if self.opts.mult_init_max > 0.0 {
113 self.opts.mult_init_max
114 } else {
115 f64::INFINITY
116 };
117 let z_floor = self.opts.mult_bound_push.max(0.0);
118 let z_nan = self.opts.bound_mult_init_val;
119 let new_curr = IteratesVector::new(
120 Rc::clone(&curr.x),
121 Rc::clone(&curr.s),
122 clone_clamped(&curr.y_c, -cap, cap, 0.0),
123 clone_clamped(&curr.y_d, -cap, cap, 0.0),
124 clone_clamped(&curr.z_l, z_floor, cap, z_nan),
125 clone_clamped(&curr.z_u, z_floor, cap, z_nan),
126 clone_clamped(&curr.v_l, z_floor, cap, z_nan),
127 clone_clamped(&curr.v_u, z_floor, cap, z_nan),
128 );
129 borrow.set_curr(new_curr);
130 }
131
132 if self.opts.target_mu > 0.0 {
133 data.borrow_mut().curr_mu = self.opts.target_mu;
134 }
135
136 true
137 }
138}
139
140fn seed_from_nlp(
148 data: &IpoptDataHandle,
149 nlp: &Rc<RefCell<dyn IpoptNlp>>,
150 opts: &WarmStartOptions,
151) -> bool {
152 if !nlp.borrow_mut().prepare_warm_start() {
153 return false;
154 }
155 let (n_x, n_s, n_yc, n_yd, n_zl, n_zu, n_vl, n_vu) = {
156 let borrow = data.borrow();
157 let c = borrow.curr.as_ref().unwrap();
158 (
159 c.x.dim(),
160 c.s.dim(),
161 c.y_c.dim(),
162 c.y_d.dim(),
163 c.z_l.dim(),
164 c.z_u.dim(),
165 c.v_l.dim(),
166 c.v_u.dim(),
167 )
168 };
169
170 let mut x = DenseVectorSpace::new(n_x).make_new_dense();
171 nlp.borrow_mut().get_starting_x(&mut x);
172 {
173 let nlp_ref = nlp.borrow();
174 push_x_into_interior(
175 &mut x,
176 &*nlp_ref.px_l(),
177 nlp_ref.x_l(),
178 &*nlp_ref.px_u(),
179 nlp_ref.x_u(),
180 opts.bound_push,
181 opts.bound_frac,
182 );
183 }
184
185 let mut s = DenseVectorSpace::new(n_s).make_new_dense();
186 nlp.borrow_mut().eval_d(&x, &mut s);
187 {
188 let nlp_ref = nlp.borrow();
189 push_x_into_interior(
190 &mut s,
191 &*nlp_ref.pd_l(),
192 nlp_ref.d_l(),
193 &*nlp_ref.pd_u(),
194 nlp_ref.d_u(),
195 opts.slack_bound_push,
196 opts.slack_bound_frac,
197 );
198 }
199
200 let mut y_c = DenseVectorSpace::new(n_yc).make_new_dense();
201 let mut y_d = DenseVectorSpace::new(n_yd).make_new_dense();
202 y_c.set(0.0);
203 y_d.set(0.0);
204 nlp.borrow_mut().get_starting_y(&mut y_c, &mut y_d);
205
206 let mut z_l = DenseVectorSpace::new(n_zl).make_new_dense();
207 let mut z_u = DenseVectorSpace::new(n_zu).make_new_dense();
208 let mut v_l = DenseVectorSpace::new(n_vl).make_new_dense();
209 let mut v_u = DenseVectorSpace::new(n_vu).make_new_dense();
210 z_l.set(0.0);
211 z_u.set(0.0);
212 v_l.set(0.0);
213 v_u.set(0.0);
214 nlp.borrow_mut()
215 .get_starting_z(&mut z_l, &mut z_u, &mut v_l, &mut v_u);
216 nlp.borrow_mut().finish_warm_start();
217
218 let iv = IteratesVector::new(
219 Rc::new(x),
220 Rc::new(s),
221 Rc::new(y_c),
222 Rc::new(y_d),
223 Rc::new(z_l),
224 Rc::new(z_u),
225 Rc::new(v_l),
226 Rc::new(v_u),
227 );
228 data.borrow_mut().set_curr(iv);
229 true
230}
231
232fn is_initialized(v: &Rc<dyn Vector>) -> bool {
233 if v.dim() == 0 {
234 return true;
235 }
236 v.as_any()
237 .downcast_ref::<DenseVector>()
238 .map(|d| d.is_initialized())
239 .unwrap_or(true)
240}
241
242fn resolve_nan_seeds(v: &mut dyn Vector, fill: f64) {
257 if v.as_any().is::<DenseVector>() {
261 let d = v.as_any_mut().downcast_mut::<DenseVector>().unwrap();
262 for e in d.values_mut() {
263 if e.is_nan() {
264 *e = fill;
265 }
266 }
267 } else if v.as_any().is::<CompoundVector>() {
268 let c = v.as_any_mut().downcast_mut::<CompoundVector>().unwrap();
269 for i in 0..c.n_comps() {
270 resolve_nan_seeds(c.comp_mut(i), fill);
271 }
272 } else {
273 debug_assert!(false, "resolve_nan_seeds: unhandled Vector implementation");
277 }
278}
279
280fn clone_clamped(v: &Rc<dyn Vector>, lo: f64, hi: f64, nan_fill: f64) -> Rc<dyn Vector> {
288 let n = v.dim();
289 if n == 0 {
290 return Rc::clone(v);
291 }
292 let mut out = v.make_new();
293 let initialized = v
294 .as_any()
295 .downcast_ref::<DenseVector>()
296 .map(|d| d.is_initialized())
297 .unwrap_or(true);
298 if initialized {
299 out.copy(&**v);
300 resolve_nan_seeds(&mut *out, nan_fill);
303 } else {
304 out.set(0.0);
305 }
306 let mut cap_hi = v.make_new();
307 cap_hi.set(hi);
308 out.element_wise_min(&*cap_hi);
309 let mut cap_lo = v.make_new();
310 cap_lo.set(lo);
311 out.element_wise_max(&*cap_lo);
312 Rc::from(out)
313}
314
315#[cfg(test)]
316mod tests_nan_seed {
317 use super::*;
318 use pounce_linalg::compound_vector::CompoundVectorSpace;
319 use pounce_linalg::dense_vector::DenseVectorSpace;
320
321 #[test]
322 fn nan_entries_take_the_fill_before_clamping() {
323 let space = DenseVectorSpace::new(3);
324 let mut d = space.make_new_dense();
325 d.values_mut().copy_from_slice(&[0.5, f64::NAN, 2e7]);
326 let v: Rc<dyn Vector> = Rc::from(d);
327 let out = clone_clamped(&v, 1e-3, 1e6, 7.0);
328 let out = out.as_any().downcast_ref::<DenseVector>().unwrap();
329 assert_eq!(out.values()[0], 0.5);
330 assert_eq!(out.values()[1], 7.0); assert_eq!(out.values()[2], 1e6); }
333
334 #[test]
340 fn nan_resolves_inside_a_compound_vector() {
341 let inner = DenseVectorSpace::new(2);
342 let space = CompoundVectorSpace::new(2, 4);
343 for icomp in 0..2 {
344 let inner = Rc::clone(&inner);
345 space.set_comp(icomp, 2, move || {
346 let mut d = inner.make_new_dense();
347 d.set(0.0);
348 Box::new(d)
349 });
350 }
351 let mut cv = CompoundVector::new(Rc::clone(&space));
352 for (icomp, vals) in [[0.5, f64::NAN], [f64::NAN, 2e7]].into_iter().enumerate() {
353 let c = cv.comp_mut(icomp as pounce_common::types::Index);
354 let d = c.as_any_mut().downcast_mut::<DenseVector>().unwrap();
355 d.values_mut().copy_from_slice(&vals);
356 }
357
358 let v: Rc<dyn Vector> = Rc::from(cv);
359 let out = clone_clamped(&v, 1e-3, 1e6, 7.0);
360
361 let out = out.as_any().downcast_ref::<CompoundVector>().unwrap();
362 let flat: Vec<f64> = (0..out.n_comps())
363 .flat_map(|i| {
364 out.comp(i)
365 .as_any()
366 .downcast_ref::<DenseVector>()
367 .unwrap()
368 .values()
369 .to_vec()
370 })
371 .collect();
372 assert_eq!(flat[0], 0.5);
373 assert_eq!(flat[1], 7.0); assert_eq!(flat[2], 7.0);
375 assert_eq!(flat[3], 1e6); }
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382 use pounce_linalg::dense_vector::DenseVectorSpace;
383
384 fn dense(n: i32, fill: f64) -> Rc<dyn Vector> {
385 let space = DenseVectorSpace::new(n);
386 let mut v = space.make_new_dense();
387 v.set(fill);
388 Rc::new(v)
389 }
390
391 #[test]
392 fn clamps_multipliers_to_cap() {
393 let v = dense(3, 1e10);
394 let out = clone_clamped(&v, 0.0, 1e6, 0.0);
395 assert_eq!(out.amax(), 1e6);
396 let v2 = dense(3, -1e10);
397 let out2 = clone_clamped(&v2, -1e6, 1e6, 0.0);
398 assert_eq!(out2.amax(), 1e6);
399 }
400
401 #[test]
402 fn clamps_bound_mults_nonneg() {
403 let v = dense(3, -5.0);
404 let out = clone_clamped(&v, 0.0, 1e6, 0.0);
405 assert_eq!(out.amax(), 0.0);
406 }
407
408 #[test]
409 fn empty_vector_short_circuits() {
410 let v = dense(0, 0.0);
411 let out = clone_clamped(&v, 0.0, 1.0, 0.0);
412 assert_eq!(out.dim(), 0);
413 }
414
415 #[test]
416 fn in_range_values_pass_through_untouched() {
417 let v = dense(3, 0.5);
418 let out = clone_clamped(&v, 0.0, 1.0, 0.0);
419 assert!((out.max() - 0.5).abs() < 1e-15);
420 assert!((out.min() - 0.5).abs() < 1e-15);
421 }
422
423 #[test]
424 fn mult_bound_push_floors_zero_bound_multipliers() {
425 let v = dense(3, 0.0);
429 let out = clone_clamped(&v, 1e-3, 1e6, 0.0);
430 assert!((out.min() - 1e-3).abs() < 1e-18);
431 let v2 = dense(3, 0.7);
433 let out2 = clone_clamped(&v2, 1e-3, 1e6, 0.0);
434 assert!((out2.max() - 0.7).abs() < 1e-15);
435 }
436
437 #[test]
438 fn uninitialized_source_collapses_to_zero() {
439 let space = DenseVectorSpace::new(4);
443 let v: Rc<dyn Vector> = Rc::new(space.make_new_dense());
444 let out = clone_clamped(&v, 0.0, 1e6, 0.0);
445 assert_eq!(out.amax(), 0.0);
446 }
447}