1use crate::ipopt_cq::IpoptCqHandle;
24use pounce_common::types::{Index, Number};
25use pounce_linalg::dense_vector::DenseVector;
26use pounce_linsol::scaling::TSymScalingMethod;
27
28pub struct SlackBasedTSymScalingMethod {
29 cq: IpoptCqHandle,
30 pub slack_scale_max: Number,
33}
34
35impl SlackBasedTSymScalingMethod {
36 pub fn new(cq: IpoptCqHandle) -> Self {
37 Self {
38 cq,
39 slack_scale_max: 1.0,
40 }
41 }
42}
43
44impl TSymScalingMethod for SlackBasedTSymScalingMethod {
45 fn compute_sym_t_scaling_factors(
46 &mut self,
47 n: Index,
48 _nnz: Index,
49 _airn: &[Index],
50 _ajcn: &[Index],
51 _a: &[Number],
52 scaling_factors: &mut [Number],
53 ) -> bool {
54 let cq_ref = self.cq.borrow();
55 let (s_template, nx, ns, nc, nd) = {
56 let data = cq_ref.data().borrow();
57 let curr = data
58 .curr
59 .as_ref()
60 .unwrap_or_else(|| panic!("SlackBasedTSymScalingMethod: curr iterate missing"));
61 (
62 curr.s.clone(),
63 curr.x.dim(),
64 curr.s.dim(),
65 curr.y_c.dim(),
66 curr.y_d.dim(),
67 )
68 };
69 debug_assert_eq!(n, nx + ns + nc + nd);
70 debug_assert_eq!(scaling_factors.len(), n as usize);
71
72 for s in scaling_factors[..nx as usize].iter_mut() {
74 *s = 1.0;
75 }
76
77 let curr_slack_s_l = cq_ref.curr_slack_s_l();
80 let curr_slack_s_u = cq_ref.curr_slack_s_u();
81 let nlp = cq_ref.nlp().borrow();
82 let pd_l = nlp.pd_l();
83 let pd_u = nlp.pd_u();
84
85 let mut tmp = s_template.make_new();
86 pd_l.mult_vector(1.0, &*curr_slack_s_l, 0.0, &mut *tmp);
87 pd_u.mult_vector(1.0, &*curr_slack_s_u, 1.0, &mut *tmp);
88
89 let mut bound = tmp.make_new();
90 bound.set(self.slack_scale_max);
91 tmp.element_wise_min(&*bound);
92
93 let dense = tmp
94 .as_any()
95 .downcast_ref::<DenseVector>()
96 .unwrap_or_else(|| {
97 panic!("SlackBasedTSymScalingMethod: slack vector is not a DenseVector")
98 });
99 let vals = dense.expanded_values();
100 scaling_factors[nx as usize..(nx + ns) as usize].copy_from_slice(&vals);
101
102 for s in scaling_factors[(nx + ns) as usize..].iter_mut() {
104 *s = 1.0;
105 }
106
107 true
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use crate::ipopt_cq::IpoptCalculatedQuantities;
115 use crate::ipopt_data::IpoptData;
116 use crate::ipopt_nlp::{IpoptNlp, Nlp};
117 use crate::iterates_vector::IteratesVector;
118 use pounce_common::types::{Index, Number};
119 use pounce_linalg::dense_vector::{DenseVector, DenseVectorSpace};
120 use pounce_linalg::special_matrix::IdentityMatrix;
121 use pounce_linalg::{Matrix, SymMatrix, Vector};
122 use std::cell::RefCell;
123 use std::rc::Rc;
124
125 fn dvec(values: &[Number]) -> DenseVector {
126 let space = DenseVectorSpace::new(values.len() as Index);
127 let mut v = space.make_new_dense();
128 if !values.is_empty() {
129 v.values_mut().copy_from_slice(values);
130 }
131 v
132 }
133
134 fn rcv(values: &[Number]) -> Rc<dyn Vector> {
135 Rc::new(dvec(values))
136 }
137
138 #[derive(Debug)]
141 struct StubNlp {
142 n_x: Index,
143 m_eq: Index,
144 m_ineq: Index,
145 x_l: DenseVector,
146 x_u: DenseVector,
147 d_l: DenseVector,
148 d_u: DenseVector,
149 px_l: Rc<dyn Matrix>,
150 px_u: Rc<dyn Matrix>,
151 pd_l: Rc<dyn Matrix>,
152 pd_u: Rc<dyn Matrix>,
153 }
154
155 impl Nlp for StubNlp {
156 fn n(&self) -> Index {
157 self.n_x
158 }
159 fn m_eq(&self) -> Index {
160 self.m_eq
161 }
162 fn m_ineq(&self) -> Index {
163 self.m_ineq
164 }
165 fn eval_f(&mut self, _x: &dyn Vector) -> Number {
166 0.0
167 }
168 fn eval_grad_f(&mut self, _x: &dyn Vector, _g: &mut dyn Vector) {}
169 fn eval_c(&mut self, _x: &dyn Vector, _c: &mut dyn Vector) {}
170 fn eval_d(&mut self, _x: &dyn Vector, _d: &mut dyn Vector) {}
171 fn eval_jac_c(&mut self, _x: &dyn Vector) -> Rc<dyn Matrix> {
172 unimplemented!()
173 }
174 fn eval_jac_d(&mut self, _x: &dyn Vector) -> Rc<dyn Matrix> {
175 unimplemented!()
176 }
177 fn eval_h(
178 &mut self,
179 _x: &dyn Vector,
180 _obj_factor: Number,
181 _y_c: &dyn Vector,
182 _y_d: &dyn Vector,
183 ) -> Rc<dyn SymMatrix> {
184 unimplemented!()
185 }
186 }
187
188 impl IpoptNlp for StubNlp {
189 fn x_l(&self) -> &dyn Vector {
190 &self.x_l
191 }
192 fn x_u(&self) -> &dyn Vector {
193 &self.x_u
194 }
195 fn d_l(&self) -> &dyn Vector {
196 &self.d_l
197 }
198 fn d_u(&self) -> &dyn Vector {
199 &self.d_u
200 }
201 fn px_l(&self) -> Rc<dyn Matrix> {
202 self.px_l.clone()
203 }
204 fn px_u(&self) -> Rc<dyn Matrix> {
205 self.px_u.clone()
206 }
207 fn pd_l(&self) -> Rc<dyn Matrix> {
208 self.pd_l.clone()
209 }
210 fn pd_u(&self) -> Rc<dyn Matrix> {
211 self.pd_u.clone()
212 }
213 }
214
215 fn build_cq(
216 x: Rc<dyn Vector>,
217 s: Rc<dyn Vector>,
218 y_c: Rc<dyn Vector>,
219 y_d: Rc<dyn Vector>,
220 nlp: StubNlp,
221 ) -> IpoptCqHandle {
222 let nx = x.dim();
225 let ns = s.dim();
226 let z = rcv(&vec![0.0; nx as usize]);
227 let v = rcv(&vec![0.0; ns as usize]);
228 let iv = IteratesVector::new(x, s, y_c, y_d, z.clone(), z, v.clone(), v);
229 let mut data = IpoptData::new();
230 data.set_curr(iv);
231 let data_handle = Rc::new(RefCell::new(data));
232 let nlp_handle: Rc<RefCell<dyn IpoptNlp>> = Rc::new(RefCell::new(nlp));
233 let cq = IpoptCalculatedQuantities::new(data_handle, nlp_handle);
234 Rc::new(RefCell::new(cq))
235 }
236
237 #[test]
238 fn slack_scaling_block_layout_with_loose_bounds_is_all_ones() {
239 let nlp = StubNlp {
245 n_x: 2,
246 m_eq: 0,
247 m_ineq: 2,
248 x_l: dvec(&[]),
249 x_u: dvec(&[]),
250 d_l: dvec(&[0.0, 0.0]),
251 d_u: dvec(&[10.0, 10.0]),
252 px_l: Rc::new(IdentityMatrix::new(0)),
253 px_u: Rc::new(IdentityMatrix::new(0)),
254 pd_l: Rc::new(IdentityMatrix::new(2)),
255 pd_u: Rc::new(IdentityMatrix::new(2)),
256 };
257 let cq = build_cq(
260 rcv(&[0.0, 0.0]),
261 rcv(&[0.5, 1.5]),
262 rcv(&[]),
263 rcv(&[0.0, 0.0]),
264 nlp,
265 );
266
267 let mut method = SlackBasedTSymScalingMethod::new(cq);
268 let mut sf = vec![0.0; 6];
269 assert!(method.compute_sym_t_scaling_factors(6, 0, &[], &[], &[], &mut sf));
270 assert_eq!(&sf[0..2], &[1.0, 1.0]);
271 assert_eq!(&sf[2..4], &[1.0, 1.0]);
272 assert_eq!(&sf[4..6], &[1.0, 1.0]);
273 }
274
275 #[test]
276 fn slack_scaling_caps_small_slack() {
277 let nlp = StubNlp {
282 n_x: 1,
283 m_eq: 0,
284 m_ineq: 2,
285 x_l: dvec(&[]),
286 x_u: dvec(&[]),
287 d_l: dvec(&[0.0, 0.0]),
288 d_u: dvec(&[0.6, 0.6]),
289 px_l: Rc::new(IdentityMatrix::new(0)),
290 px_u: Rc::new(IdentityMatrix::new(0)),
291 pd_l: Rc::new(IdentityMatrix::new(2)),
292 pd_u: Rc::new(IdentityMatrix::new(2)),
293 };
294 let cq = build_cq(
295 rcv(&[0.0]),
296 rcv(&[0.5, 0.5]),
297 rcv(&[]),
298 rcv(&[0.0, 0.0]),
299 nlp,
300 );
301
302 let mut method = SlackBasedTSymScalingMethod::new(cq);
303 let mut sf = vec![0.0; 5];
304 assert!(method.compute_sym_t_scaling_factors(5, 0, &[], &[], &[], &mut sf));
305 assert_eq!(sf[0], 1.0);
306 assert!((sf[1] - 0.6).abs() < 1e-15);
307 assert!((sf[2] - 0.6).abs() < 1e-15);
308 assert_eq!(&sf[3..5], &[1.0, 1.0]);
309 }
310
311 #[test]
312 fn slack_scaling_with_only_lower_bounds_uses_pd_l_only() {
313 let nlp = StubNlp {
318 n_x: 1,
319 m_eq: 1,
320 m_ineq: 2,
321 x_l: dvec(&[]),
322 x_u: dvec(&[]),
323 d_l: dvec(&[0.1, 0.1]),
324 d_u: dvec(&[]),
325 px_l: Rc::new(IdentityMatrix::new(0)),
326 px_u: Rc::new(IdentityMatrix::new(0)),
327 pd_l: Rc::new(IdentityMatrix::new(2)),
328 pd_u: Rc::new(zero_matrix(2, 0)),
331 };
332 let cq = build_cq(
333 rcv(&[0.0]),
334 rcv(&[0.4, 0.7]),
335 rcv(&[0.0]),
336 rcv(&[0.0, 0.0]),
337 nlp,
338 );
339
340 let mut method = SlackBasedTSymScalingMethod::new(cq);
341 let mut sf = vec![0.0; 6];
343 assert!(method.compute_sym_t_scaling_factors(6, 0, &[], &[], &[], &mut sf));
344 assert_eq!(sf[0], 1.0);
345 assert!((sf[1] - 0.3).abs() < 1e-15);
346 assert!((sf[2] - 0.6).abs() < 1e-15);
347 assert_eq!(&sf[3..6], &[1.0, 1.0, 1.0]);
348 }
349
350 fn zero_matrix(rows: Index, cols: Index) -> pounce_linalg::special_matrix::ZeroMatrix {
351 pounce_linalg::special_matrix::ZeroMatrix::new(rows, cols)
352 }
353}