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