1use crate::alg_builder::WarmStartOptions;
27use crate::init::default::push_x_into_interior;
28use crate::init::r#trait::IterateInitializer;
29use crate::ipopt_cq::IpoptCqHandle;
30use crate::ipopt_data::IpoptDataHandle;
31use crate::ipopt_nlp::IpoptNlp;
32use crate::iterates_vector::IteratesVector;
33use crate::kkt::aug_system_solver::AugSystemSolver;
34use pounce_linalg::dense_vector::{DenseVector, DenseVectorSpace};
35use pounce_linalg::Vector;
36use std::cell::RefCell;
37use std::rc::Rc;
38
39pub struct WarmStartIterateInitializer {
40 opts: WarmStartOptions,
41}
42
43impl WarmStartIterateInitializer {
44 pub fn new() -> Self {
45 Self {
46 opts: WarmStartOptions::default(),
47 }
48 }
49
50 pub fn with_options(opts: WarmStartOptions) -> Self {
51 Self { opts }
52 }
53}
54
55impl Default for WarmStartIterateInitializer {
56 fn default() -> Self {
57 Self::new()
58 }
59}
60
61impl IterateInitializer for WarmStartIterateInitializer {
62 fn set_initial_iterates(
63 &mut self,
64 data: &IpoptDataHandle,
65 _cq: &IpoptCqHandle,
66 nlp: &Rc<RefCell<dyn IpoptNlp>>,
67 _aug_solver: &mut dyn AugSystemSolver,
68 ) -> bool {
69 let needs_seed_from_nlp = {
76 let borrow = data.borrow();
77 match borrow.curr.as_ref() {
78 None => return false,
79 Some(c) => !is_initialized(&c.x),
80 }
81 };
82
83 if needs_seed_from_nlp {
84 seed_from_nlp(data, nlp, &self.opts);
85 }
86
87 if self.opts.mult_init_max > 0.0 {
88 let mut borrow = data.borrow_mut();
93 let curr = borrow.curr.as_ref().unwrap();
94 let cap = self.opts.mult_init_max;
95 let new_curr = IteratesVector::new(
96 Rc::clone(&curr.x),
97 Rc::clone(&curr.s),
98 clone_clamped(&curr.y_c, -cap, cap),
99 clone_clamped(&curr.y_d, -cap, cap),
100 clone_clamped(&curr.z_l, 0.0, cap),
101 clone_clamped(&curr.z_u, 0.0, cap),
102 clone_clamped(&curr.v_l, 0.0, cap),
103 clone_clamped(&curr.v_u, 0.0, cap),
104 );
105 borrow.set_curr(new_curr);
106 }
107
108 if self.opts.target_mu > 0.0 {
109 data.borrow_mut().curr_mu = self.opts.target_mu;
110 }
111
112 true
113 }
114}
115
116fn seed_from_nlp(data: &IpoptDataHandle, nlp: &Rc<RefCell<dyn IpoptNlp>>, opts: &WarmStartOptions) {
124 let (n_x, n_s, n_yc, n_yd, n_zl, n_zu, n_vl, n_vu) = {
125 let borrow = data.borrow();
126 let c = borrow.curr.as_ref().unwrap();
127 (
128 c.x.dim(),
129 c.s.dim(),
130 c.y_c.dim(),
131 c.y_d.dim(),
132 c.z_l.dim(),
133 c.z_u.dim(),
134 c.v_l.dim(),
135 c.v_u.dim(),
136 )
137 };
138
139 let mut x = DenseVectorSpace::new(n_x).make_new_dense();
140 nlp.borrow_mut().get_starting_x(&mut x);
141 {
142 let nlp_ref = nlp.borrow();
143 push_x_into_interior(
144 &mut x,
145 &*nlp_ref.px_l(),
146 nlp_ref.x_l(),
147 &*nlp_ref.px_u(),
148 nlp_ref.x_u(),
149 opts.bound_push,
150 opts.bound_frac,
151 );
152 }
153
154 let mut s = DenseVectorSpace::new(n_s).make_new_dense();
155 nlp.borrow_mut().eval_d(&x, &mut s);
156 {
157 let nlp_ref = nlp.borrow();
158 push_x_into_interior(
159 &mut s,
160 &*nlp_ref.pd_l(),
161 nlp_ref.d_l(),
162 &*nlp_ref.pd_u(),
163 nlp_ref.d_u(),
164 opts.slack_bound_push,
165 opts.slack_bound_frac,
166 );
167 }
168
169 let mut y_c = DenseVectorSpace::new(n_yc).make_new_dense();
170 let mut y_d = DenseVectorSpace::new(n_yd).make_new_dense();
171 y_c.set(0.0);
172 y_d.set(0.0);
173 nlp.borrow_mut().get_starting_y(&mut y_c, &mut y_d);
174
175 let mut z_l = DenseVectorSpace::new(n_zl).make_new_dense();
176 let mut z_u = DenseVectorSpace::new(n_zu).make_new_dense();
177 let mut v_l = DenseVectorSpace::new(n_vl).make_new_dense();
178 let mut v_u = DenseVectorSpace::new(n_vu).make_new_dense();
179 z_l.set(0.0);
180 z_u.set(0.0);
181 v_l.set(0.0);
182 v_u.set(0.0);
183 nlp.borrow_mut()
184 .get_starting_z(&mut z_l, &mut z_u, &mut v_l, &mut v_u);
185
186 let iv = IteratesVector::new(
187 Rc::new(x),
188 Rc::new(s),
189 Rc::new(y_c),
190 Rc::new(y_d),
191 Rc::new(z_l),
192 Rc::new(z_u),
193 Rc::new(v_l),
194 Rc::new(v_u),
195 );
196 data.borrow_mut().set_curr(iv);
197}
198
199fn is_initialized(v: &Rc<dyn Vector>) -> bool {
200 if v.dim() == 0 {
201 return true;
202 }
203 v.as_any()
204 .downcast_ref::<DenseVector>()
205 .map(|d| d.is_initialized())
206 .unwrap_or(true)
207}
208
209fn clone_clamped(v: &Rc<dyn Vector>, lo: f64, hi: f64) -> Rc<dyn Vector> {
217 let n = v.dim();
218 if n == 0 {
219 return Rc::clone(v);
220 }
221 let mut out = v.make_new();
222 let initialized = v
223 .as_any()
224 .downcast_ref::<DenseVector>()
225 .map(|d| d.is_initialized())
226 .unwrap_or(true);
227 if initialized {
228 out.copy(&**v);
229 } else {
230 out.set(0.0);
231 }
232 let mut cap_hi = v.make_new();
233 cap_hi.set(hi);
234 out.element_wise_min(&*cap_hi);
235 let mut cap_lo = v.make_new();
236 cap_lo.set(lo);
237 out.element_wise_max(&*cap_lo);
238 Rc::from(out)
239}
240
241#[cfg(test)]
242mod tests {
243 use super::*;
244 use pounce_linalg::dense_vector::DenseVectorSpace;
245
246 fn dense(n: i32, fill: f64) -> Rc<dyn Vector> {
247 let space = DenseVectorSpace::new(n);
248 let mut v = space.make_new_dense();
249 v.set(fill);
250 Rc::new(v)
251 }
252
253 #[test]
254 fn clamps_multipliers_to_cap() {
255 let v = dense(3, 1e10);
256 let out = clone_clamped(&v, 0.0, 1e6);
257 assert_eq!(out.amax(), 1e6);
258 let v2 = dense(3, -1e10);
259 let out2 = clone_clamped(&v2, -1e6, 1e6);
260 assert_eq!(out2.amax(), 1e6);
261 }
262
263 #[test]
264 fn clamps_bound_mults_nonneg() {
265 let v = dense(3, -5.0);
266 let out = clone_clamped(&v, 0.0, 1e6);
267 assert_eq!(out.amax(), 0.0);
268 }
269
270 #[test]
271 fn empty_vector_short_circuits() {
272 let v = dense(0, 0.0);
273 let out = clone_clamped(&v, 0.0, 1.0);
274 assert_eq!(out.dim(), 0);
275 }
276
277 #[test]
278 fn in_range_values_pass_through_untouched() {
279 let v = dense(3, 0.5);
280 let out = clone_clamped(&v, 0.0, 1.0);
281 assert!((out.max() - 0.5).abs() < 1e-15);
282 assert!((out.min() - 0.5).abs() < 1e-15);
283 }
284
285 #[test]
286 fn uninitialized_source_collapses_to_zero() {
287 let space = DenseVectorSpace::new(4);
291 let v: Rc<dyn Vector> = Rc::new(space.make_new_dense());
292 let out = clone_clamped(&v, 0.0, 1e6);
293 assert_eq!(out.amax(), 0.0);
294 }
295}