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::dense_vector::{DenseVector, DenseVectorSpace};
40use std::cell::RefCell;
41use std::rc::Rc;
42
43pub struct WarmStartIterateInitializer {
44 opts: WarmStartOptions,
45}
46
47impl WarmStartIterateInitializer {
48 pub fn new() -> Self {
49 Self {
50 opts: WarmStartOptions::default(),
51 }
52 }
53
54 pub fn with_options(opts: WarmStartOptions) -> Self {
55 Self { opts }
56 }
57}
58
59impl Default for WarmStartIterateInitializer {
60 fn default() -> Self {
61 Self::new()
62 }
63}
64
65impl IterateInitializer for WarmStartIterateInitializer {
66 fn set_initial_iterates(
67 &mut self,
68 data: &IpoptDataHandle,
69 _cq: &IpoptCqHandle,
70 nlp: &Rc<RefCell<dyn IpoptNlp>>,
71 _aug_solver: &mut dyn AugSystemSolver,
72 ) -> bool {
73 let needs_seed_from_nlp = {
80 let borrow = data.borrow();
81 match borrow.curr.as_ref() {
82 None => return false,
83 Some(c) => !is_initialized(&c.x),
84 }
85 };
86
87 if needs_seed_from_nlp {
88 seed_from_nlp(data, nlp, &self.opts);
89 }
90
91 if self.opts.mult_init_max > 0.0 || self.opts.mult_bound_push > 0.0 {
92 let mut borrow = data.borrow_mut();
101 let curr = borrow.curr.as_ref().unwrap();
102 let cap = if self.opts.mult_init_max > 0.0 {
103 self.opts.mult_init_max
104 } else {
105 f64::INFINITY
106 };
107 let z_floor = self.opts.mult_bound_push.max(0.0);
108 let new_curr = IteratesVector::new(
109 Rc::clone(&curr.x),
110 Rc::clone(&curr.s),
111 clone_clamped(&curr.y_c, -cap, cap),
112 clone_clamped(&curr.y_d, -cap, cap),
113 clone_clamped(&curr.z_l, z_floor, cap),
114 clone_clamped(&curr.z_u, z_floor, cap),
115 clone_clamped(&curr.v_l, z_floor, cap),
116 clone_clamped(&curr.v_u, z_floor, cap),
117 );
118 borrow.set_curr(new_curr);
119 }
120
121 if self.opts.target_mu > 0.0 {
122 data.borrow_mut().curr_mu = self.opts.target_mu;
123 }
124
125 true
126 }
127}
128
129fn seed_from_nlp(data: &IpoptDataHandle, nlp: &Rc<RefCell<dyn IpoptNlp>>, opts: &WarmStartOptions) {
137 let (n_x, n_s, n_yc, n_yd, n_zl, n_zu, n_vl, n_vu) = {
138 let borrow = data.borrow();
139 let c = borrow.curr.as_ref().unwrap();
140 (
141 c.x.dim(),
142 c.s.dim(),
143 c.y_c.dim(),
144 c.y_d.dim(),
145 c.z_l.dim(),
146 c.z_u.dim(),
147 c.v_l.dim(),
148 c.v_u.dim(),
149 )
150 };
151
152 let mut x = DenseVectorSpace::new(n_x).make_new_dense();
153 nlp.borrow_mut().get_starting_x(&mut x);
154 {
155 let nlp_ref = nlp.borrow();
156 push_x_into_interior(
157 &mut x,
158 &*nlp_ref.px_l(),
159 nlp_ref.x_l(),
160 &*nlp_ref.px_u(),
161 nlp_ref.x_u(),
162 opts.bound_push,
163 opts.bound_frac,
164 );
165 }
166
167 let mut s = DenseVectorSpace::new(n_s).make_new_dense();
168 nlp.borrow_mut().eval_d(&x, &mut s);
169 {
170 let nlp_ref = nlp.borrow();
171 push_x_into_interior(
172 &mut s,
173 &*nlp_ref.pd_l(),
174 nlp_ref.d_l(),
175 &*nlp_ref.pd_u(),
176 nlp_ref.d_u(),
177 opts.slack_bound_push,
178 opts.slack_bound_frac,
179 );
180 }
181
182 let mut y_c = DenseVectorSpace::new(n_yc).make_new_dense();
183 let mut y_d = DenseVectorSpace::new(n_yd).make_new_dense();
184 y_c.set(0.0);
185 y_d.set(0.0);
186 nlp.borrow_mut().get_starting_y(&mut y_c, &mut y_d);
187
188 let mut z_l = DenseVectorSpace::new(n_zl).make_new_dense();
189 let mut z_u = DenseVectorSpace::new(n_zu).make_new_dense();
190 let mut v_l = DenseVectorSpace::new(n_vl).make_new_dense();
191 let mut v_u = DenseVectorSpace::new(n_vu).make_new_dense();
192 z_l.set(0.0);
193 z_u.set(0.0);
194 v_l.set(0.0);
195 v_u.set(0.0);
196 nlp.borrow_mut()
197 .get_starting_z(&mut z_l, &mut z_u, &mut v_l, &mut v_u);
198
199 let iv = IteratesVector::new(
200 Rc::new(x),
201 Rc::new(s),
202 Rc::new(y_c),
203 Rc::new(y_d),
204 Rc::new(z_l),
205 Rc::new(z_u),
206 Rc::new(v_l),
207 Rc::new(v_u),
208 );
209 data.borrow_mut().set_curr(iv);
210}
211
212fn is_initialized(v: &Rc<dyn Vector>) -> bool {
213 if v.dim() == 0 {
214 return true;
215 }
216 v.as_any()
217 .downcast_ref::<DenseVector>()
218 .map(|d| d.is_initialized())
219 .unwrap_or(true)
220}
221
222fn clone_clamped(v: &Rc<dyn Vector>, lo: f64, hi: f64) -> Rc<dyn Vector> {
230 let n = v.dim();
231 if n == 0 {
232 return Rc::clone(v);
233 }
234 let mut out = v.make_new();
235 let initialized = v
236 .as_any()
237 .downcast_ref::<DenseVector>()
238 .map(|d| d.is_initialized())
239 .unwrap_or(true);
240 if initialized {
241 out.copy(&**v);
242 } else {
243 out.set(0.0);
244 }
245 let mut cap_hi = v.make_new();
246 cap_hi.set(hi);
247 out.element_wise_min(&*cap_hi);
248 let mut cap_lo = v.make_new();
249 cap_lo.set(lo);
250 out.element_wise_max(&*cap_lo);
251 Rc::from(out)
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257 use pounce_linalg::dense_vector::DenseVectorSpace;
258
259 fn dense(n: i32, fill: f64) -> Rc<dyn Vector> {
260 let space = DenseVectorSpace::new(n);
261 let mut v = space.make_new_dense();
262 v.set(fill);
263 Rc::new(v)
264 }
265
266 #[test]
267 fn clamps_multipliers_to_cap() {
268 let v = dense(3, 1e10);
269 let out = clone_clamped(&v, 0.0, 1e6);
270 assert_eq!(out.amax(), 1e6);
271 let v2 = dense(3, -1e10);
272 let out2 = clone_clamped(&v2, -1e6, 1e6);
273 assert_eq!(out2.amax(), 1e6);
274 }
275
276 #[test]
277 fn clamps_bound_mults_nonneg() {
278 let v = dense(3, -5.0);
279 let out = clone_clamped(&v, 0.0, 1e6);
280 assert_eq!(out.amax(), 0.0);
281 }
282
283 #[test]
284 fn empty_vector_short_circuits() {
285 let v = dense(0, 0.0);
286 let out = clone_clamped(&v, 0.0, 1.0);
287 assert_eq!(out.dim(), 0);
288 }
289
290 #[test]
291 fn in_range_values_pass_through_untouched() {
292 let v = dense(3, 0.5);
293 let out = clone_clamped(&v, 0.0, 1.0);
294 assert!((out.max() - 0.5).abs() < 1e-15);
295 assert!((out.min() - 0.5).abs() < 1e-15);
296 }
297
298 #[test]
299 fn mult_bound_push_floors_zero_bound_multipliers() {
300 let v = dense(3, 0.0);
304 let out = clone_clamped(&v, 1e-3, 1e6);
305 assert!((out.min() - 1e-3).abs() < 1e-18);
306 let v2 = dense(3, 0.7);
308 let out2 = clone_clamped(&v2, 1e-3, 1e6);
309 assert!((out2.max() - 0.7).abs() < 1e-15);
310 }
311
312 #[test]
313 fn uninitialized_source_collapses_to_zero() {
314 let space = DenseVectorSpace::new(4);
318 let v: Rc<dyn Vector> = Rc::new(space.make_new_dense());
319 let out = clone_clamped(&v, 0.0, 1e6);
320 assert_eq!(out.amax(), 0.0);
321 }
322}