Skip to main content

pounce_algorithm/
iterates_vector.rs

1//! Eight-component iterate — port of
2//! `Algorithm/IpIteratesVector.{hpp,cpp}`.
3//!
4//! Concrete struct with named fields rather than upstream's
5//! `CompoundVector` slot-by-index. Same components, same indexing
6//! convention:
7//!
8//! | slot | name | meaning                                  |
9//! |------|------|------------------------------------------|
10//! |  0   | x    | primal variables                          |
11//! |  1   | s    | inequality slacks                         |
12//! |  2   | y_c  | equality multipliers                      |
13//! |  3   | y_d  | inequality multipliers                    |
14//! |  4   | z_l  | x lower-bound multipliers                 |
15//! |  5   | z_u  | x upper-bound multipliers                 |
16//! |  6   | v_l  | s lower-bound multipliers                 |
17//! |  7   | v_u  | s upper-bound multipliers                 |
18//!
19//! Components are `Rc<dyn Vector>` to keep upstream's shared-ownership
20//! semantics (the same `x` lives in `curr`, `delta`, etc., until
21//! someone replaces it via `set_*`).
22
23use pounce_linalg::Vector;
24use std::rc::Rc;
25
26/// Eight-component iterate vector. Cheap to clone via `Rc`.
27#[derive(Clone)]
28pub struct IteratesVector {
29    pub x: Rc<dyn Vector>,
30    pub s: Rc<dyn Vector>,
31    pub y_c: Rc<dyn Vector>,
32    pub y_d: Rc<dyn Vector>,
33    pub z_l: Rc<dyn Vector>,
34    pub z_u: Rc<dyn Vector>,
35    pub v_l: Rc<dyn Vector>,
36    pub v_u: Rc<dyn Vector>,
37}
38
39impl std::fmt::Debug for IteratesVector {
40    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41        f.debug_struct("IteratesVector")
42            .field("x_dim", &self.x.dim())
43            .field("s_dim", &self.s.dim())
44            .field("y_c_dim", &self.y_c.dim())
45            .field("y_d_dim", &self.y_d.dim())
46            .field("z_l_dim", &self.z_l.dim())
47            .field("z_u_dim", &self.z_u.dim())
48            .field("v_l_dim", &self.v_l.dim())
49            .field("v_u_dim", &self.v_u.dim())
50            .finish()
51    }
52}
53
54impl IteratesVector {
55    /// Construct from eight already-allocated component vectors.
56    #[allow(clippy::too_many_arguments)]
57    pub fn new(
58        x: Rc<dyn Vector>,
59        s: Rc<dyn Vector>,
60        y_c: Rc<dyn Vector>,
61        y_d: Rc<dyn Vector>,
62        z_l: Rc<dyn Vector>,
63        z_u: Rc<dyn Vector>,
64        v_l: Rc<dyn Vector>,
65        v_u: Rc<dyn Vector>,
66    ) -> Self {
67        Self {
68            x,
69            s,
70            y_c,
71            y_d,
72            z_l,
73            z_u,
74            v_l,
75            v_u,
76        }
77    }
78
79    /// Total dimension across all eight components.
80    pub fn dim(&self) -> i32 {
81        self.x.dim()
82            + self.s.dim()
83            + self.y_c.dim()
84            + self.y_d.dim()
85            + self.z_l.dim()
86            + self.z_u.dim()
87            + self.v_l.dim()
88            + self.v_u.dim()
89    }
90
91    /// Max-norm across all eight components — port of
92    /// `IteratesVector::Amax()` (which itself is `CompoundVector::Amax`,
93    /// the max of per-block `Amax`).
94    pub fn amax(&self) -> pounce_common::types::Number {
95        let mut m = self.x.amax();
96        for v in [
97            &self.s, &self.y_c, &self.y_d, &self.z_l, &self.z_u, &self.v_l, &self.v_u,
98        ] {
99            let a = v.amax();
100            if a > m {
101                m = a;
102            }
103        }
104        m
105    }
106
107    /// Allocate a fresh, zero-initialized iterate with the same shape.
108    /// Equivalent to upstream `MakeNewIteratesVector(true)`.
109    pub fn make_new_zeroed(&self) -> IteratesVectorMut {
110        IteratesVectorMut {
111            x: self.x.make_new(),
112            s: self.s.make_new(),
113            y_c: self.y_c.make_new(),
114            y_d: self.y_d.make_new(),
115            z_l: self.z_l.make_new(),
116            z_u: self.z_u.make_new(),
117            v_l: self.v_l.make_new(),
118            v_u: self.v_u.make_new(),
119        }
120    }
121
122    /// Deep copy — equivalent to upstream `MakeNewIteratesVectorCopy()`.
123    pub fn deep_copy(&self) -> IteratesVectorMut {
124        let mut out = self.make_new_zeroed();
125        out.x.copy(&*self.x);
126        out.s.copy(&*self.s);
127        out.y_c.copy(&*self.y_c);
128        out.y_d.copy(&*self.y_d);
129        out.z_l.copy(&*self.z_l);
130        out.z_u.copy(&*self.z_u);
131        out.v_l.copy(&*self.v_l);
132        out.v_u.copy(&*self.v_u);
133        out
134    }
135}
136
137/// Owned, mutable variant — used as the working-storage form of an
138/// IteratesVector (typical use: a freshly-allocated solution slot the
139/// solver writes into). Convertible into `IteratesVector` via `freeze`.
140pub struct IteratesVectorMut {
141    pub x: Box<dyn Vector>,
142    pub s: Box<dyn Vector>,
143    pub y_c: Box<dyn Vector>,
144    pub y_d: Box<dyn Vector>,
145    pub z_l: Box<dyn Vector>,
146    pub z_u: Box<dyn Vector>,
147    pub v_l: Box<dyn Vector>,
148    pub v_u: Box<dyn Vector>,
149}
150
151impl IteratesVectorMut {
152    /// Convert into the shareable `Rc`-backed form.
153    pub fn freeze(self) -> IteratesVector {
154        IteratesVector::new(
155            Rc::from(self.x),
156            Rc::from(self.s),
157            Rc::from(self.y_c),
158            Rc::from(self.y_d),
159            Rc::from(self.z_l),
160            Rc::from(self.z_u),
161            Rc::from(self.v_l),
162            Rc::from(self.v_u),
163        )
164    }
165
166    pub fn amax(&self) -> pounce_common::types::Number {
167        let mut m = self.x.amax();
168        for v in [
169            &self.s, &self.y_c, &self.y_d, &self.z_l, &self.z_u, &self.v_l, &self.v_u,
170        ] {
171            let a = v.amax();
172            if a > m {
173                m = a;
174            }
175        }
176        m
177    }
178
179    /// Scale every component by `alpha` — port of `IteratesVector::Scal`.
180    pub fn scal(&mut self, alpha: pounce_common::types::Number) {
181        self.x.scal(alpha);
182        self.s.scal(alpha);
183        self.y_c.scal(alpha);
184        self.y_d.scal(alpha);
185        self.z_l.scal(alpha);
186        self.z_u.scal(alpha);
187        self.v_l.scal(alpha);
188        self.v_u.scal(alpha);
189    }
190
191    /// `self += alpha * other` per component — port of `IteratesVector::Axpy`.
192    pub fn axpy(&mut self, alpha: pounce_common::types::Number, other: &IteratesVector) {
193        self.x.axpy(alpha, &*other.x);
194        self.s.axpy(alpha, &*other.s);
195        self.y_c.axpy(alpha, &*other.y_c);
196        self.y_d.axpy(alpha, &*other.y_d);
197        self.z_l.axpy(alpha, &*other.z_l);
198        self.z_u.axpy(alpha, &*other.z_u);
199        self.v_l.axpy(alpha, &*other.v_l);
200        self.v_u.axpy(alpha, &*other.v_u);
201    }
202
203    /// `self = a*self + b*other` per component — port of
204    /// `IteratesVector::AddOneVector` (when called on `self`).
205    pub fn add_one_vector(
206        &mut self,
207        a: pounce_common::types::Number,
208        other: &IteratesVector,
209        b: pounce_common::types::Number,
210    ) {
211        self.x.add_one_vector(a, &*other.x, b);
212        self.s.add_one_vector(a, &*other.s, b);
213        self.y_c.add_one_vector(a, &*other.y_c, b);
214        self.y_d.add_one_vector(a, &*other.y_d, b);
215        self.z_l.add_one_vector(a, &*other.z_l, b);
216        self.z_u.add_one_vector(a, &*other.z_u, b);
217        self.v_l.add_one_vector(a, &*other.v_l, b);
218        self.v_u.add_one_vector(a, &*other.v_u, b);
219    }
220}
221
222#[cfg(test)]
223mod tests {
224    use super::*;
225    use pounce_linalg::dense_vector::DenseVectorSpace;
226
227    fn zero_vec(n: i32) -> Rc<dyn Vector> {
228        let space = DenseVectorSpace::new(n);
229        Rc::new(space.make_new_dense())
230    }
231
232    #[test]
233    fn iterates_vector_dim_sums_components() {
234        let iv = IteratesVector::new(
235            zero_vec(4),
236            zero_vec(1),
237            zero_vec(1),
238            zero_vec(1),
239            zero_vec(4),
240            zero_vec(4),
241            zero_vec(1),
242            zero_vec(1),
243        );
244        assert_eq!(iv.dim(), 4 + 1 + 1 + 1 + 4 + 4 + 1 + 1);
245    }
246}