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    /// Clone this iterate with `x` swapped out and every other
80    /// component shared. `Rc` makes it a pointer copy, so this is the
81    /// cheap way to re-stage a candidate `x` for a CQ evaluation
82    /// without rebuilding the other seven blocks.
83    pub fn with_x(&self, x: Rc<dyn Vector>) -> Self {
84        Self { x, ..self.clone() }
85    }
86
87    /// Total dimension across all eight components.
88    pub fn dim(&self) -> i32 {
89        self.x.dim()
90            + self.s.dim()
91            + self.y_c.dim()
92            + self.y_d.dim()
93            + self.z_l.dim()
94            + self.z_u.dim()
95            + self.v_l.dim()
96            + self.v_u.dim()
97    }
98
99    /// Max-norm across all eight components — port of
100    /// `IteratesVector::Amax()` (which itself is `CompoundVector::Amax`,
101    /// the max of per-block `Amax`).
102    pub fn amax(&self) -> pounce_common::types::Number {
103        let mut m = self.x.amax();
104        for v in [
105            &self.s, &self.y_c, &self.y_d, &self.z_l, &self.z_u, &self.v_l, &self.v_u,
106        ] {
107            let a = v.amax();
108            if a > m {
109                m = a;
110            }
111        }
112        m
113    }
114
115    /// Allocate a fresh, zero-initialized iterate with the same shape.
116    /// Equivalent to upstream `MakeNewIteratesVector(true)`.
117    pub fn make_new_zeroed(&self) -> IteratesVectorMut {
118        IteratesVectorMut {
119            x: self.x.make_new(),
120            s: self.s.make_new(),
121            y_c: self.y_c.make_new(),
122            y_d: self.y_d.make_new(),
123            z_l: self.z_l.make_new(),
124            z_u: self.z_u.make_new(),
125            v_l: self.v_l.make_new(),
126            v_u: self.v_u.make_new(),
127        }
128    }
129
130    /// Deep copy — equivalent to upstream `MakeNewIteratesVectorCopy()`.
131    pub fn deep_copy(&self) -> IteratesVectorMut {
132        let mut out = self.make_new_zeroed();
133        out.x.copy(&*self.x);
134        out.s.copy(&*self.s);
135        out.y_c.copy(&*self.y_c);
136        out.y_d.copy(&*self.y_d);
137        out.z_l.copy(&*self.z_l);
138        out.z_u.copy(&*self.z_u);
139        out.v_l.copy(&*self.v_l);
140        out.v_u.copy(&*self.v_u);
141        out
142    }
143}
144
145/// Owned, mutable variant — used as the working-storage form of an
146/// IteratesVector (typical use: a freshly-allocated solution slot the
147/// solver writes into). Convertible into `IteratesVector` via `freeze`.
148pub struct IteratesVectorMut {
149    pub x: Box<dyn Vector>,
150    pub s: Box<dyn Vector>,
151    pub y_c: Box<dyn Vector>,
152    pub y_d: Box<dyn Vector>,
153    pub z_l: Box<dyn Vector>,
154    pub z_u: Box<dyn Vector>,
155    pub v_l: Box<dyn Vector>,
156    pub v_u: Box<dyn Vector>,
157}
158
159impl IteratesVectorMut {
160    /// Convert into the shareable `Rc`-backed form.
161    pub fn freeze(self) -> IteratesVector {
162        IteratesVector::new(
163            Rc::from(self.x),
164            Rc::from(self.s),
165            Rc::from(self.y_c),
166            Rc::from(self.y_d),
167            Rc::from(self.z_l),
168            Rc::from(self.z_u),
169            Rc::from(self.v_l),
170            Rc::from(self.v_u),
171        )
172    }
173
174    pub fn amax(&self) -> pounce_common::types::Number {
175        let mut m = self.x.amax();
176        for v in [
177            &self.s, &self.y_c, &self.y_d, &self.z_l, &self.z_u, &self.v_l, &self.v_u,
178        ] {
179            let a = v.amax();
180            if a > m {
181                m = a;
182            }
183        }
184        m
185    }
186
187    /// Scale every component by `alpha` — port of `IteratesVector::Scal`.
188    pub fn scal(&mut self, alpha: pounce_common::types::Number) {
189        self.x.scal(alpha);
190        self.s.scal(alpha);
191        self.y_c.scal(alpha);
192        self.y_d.scal(alpha);
193        self.z_l.scal(alpha);
194        self.z_u.scal(alpha);
195        self.v_l.scal(alpha);
196        self.v_u.scal(alpha);
197    }
198
199    /// `self += alpha * other` per component — port of `IteratesVector::Axpy`.
200    pub fn axpy(&mut self, alpha: pounce_common::types::Number, other: &IteratesVector) {
201        self.x.axpy(alpha, &*other.x);
202        self.s.axpy(alpha, &*other.s);
203        self.y_c.axpy(alpha, &*other.y_c);
204        self.y_d.axpy(alpha, &*other.y_d);
205        self.z_l.axpy(alpha, &*other.z_l);
206        self.z_u.axpy(alpha, &*other.z_u);
207        self.v_l.axpy(alpha, &*other.v_l);
208        self.v_u.axpy(alpha, &*other.v_u);
209    }
210
211    /// `self = a*self + b*other` per component — port of
212    /// `IteratesVector::AddOneVector` (when called on `self`).
213    pub fn add_one_vector(
214        &mut self,
215        a: pounce_common::types::Number,
216        other: &IteratesVector,
217        b: pounce_common::types::Number,
218    ) {
219        self.x.add_one_vector(a, &*other.x, b);
220        self.s.add_one_vector(a, &*other.s, b);
221        self.y_c.add_one_vector(a, &*other.y_c, b);
222        self.y_d.add_one_vector(a, &*other.y_d, b);
223        self.z_l.add_one_vector(a, &*other.z_l, b);
224        self.z_u.add_one_vector(a, &*other.z_u, b);
225        self.v_l.add_one_vector(a, &*other.v_l, b);
226        self.v_u.add_one_vector(a, &*other.v_u, b);
227    }
228}
229
230#[cfg(test)]
231mod tests {
232    use super::*;
233    use pounce_linalg::dense_vector::DenseVectorSpace;
234
235    fn zero_vec(n: i32) -> Rc<dyn Vector> {
236        let space = DenseVectorSpace::new(n);
237        Rc::new(space.make_new_dense())
238    }
239
240    #[test]
241    fn iterates_vector_dim_sums_components() {
242        let iv = IteratesVector::new(
243            zero_vec(4),
244            zero_vec(1),
245            zero_vec(1),
246            zero_vec(1),
247            zero_vec(4),
248            zero_vec(4),
249            zero_vec(1),
250            zero_vec(1),
251        );
252        assert_eq!(iv.dim(), 4 + 1 + 1 + 1 + 4 + 4 + 1 + 1);
253    }
254}