oxiz_math/lp/
basis_update.rs1#[allow(unused_imports)]
18use crate::prelude::*;
19use num_rational::BigRational;
20use num_traits::One;
21
22pub type VarId = usize;
24
25#[derive(Debug, Clone)]
27pub struct Basis {
28 pub basic_vars: Vec<VarId>,
30 pub nonbasic_vars: Vec<VarId>,
32 pub basis_matrix: FxHashMap<(usize, usize), BigRational>,
34}
35
36impl Basis {
37 pub fn new(basic_vars: Vec<VarId>, nonbasic_vars: Vec<VarId>) -> Self {
39 Self {
40 basic_vars,
41 nonbasic_vars,
42 basis_matrix: FxHashMap::default(),
43 }
44 }
45
46 pub fn size(&self) -> usize {
48 self.basic_vars.len()
49 }
50
51 pub fn is_basic(&self, var: VarId) -> bool {
53 self.basic_vars.contains(&var)
54 }
55}
56
57#[derive(Debug, Clone)]
59pub struct EtaMatrix {
60 pub column: usize,
62 pub eta_vector: FxHashMap<usize, BigRational>,
64}
65
66#[derive(Debug, Clone)]
68pub struct BasisUpdateConfig {
69 pub use_forrest_tomlin: bool,
71 pub refactorize_freq: usize,
73 pub steepest_edge: bool,
75}
76
77impl Default for BasisUpdateConfig {
78 fn default() -> Self {
79 Self {
80 use_forrest_tomlin: true,
81 refactorize_freq: 100,
82 steepest_edge: true,
83 }
84 }
85}
86
87#[derive(Debug, Clone, Default)]
89pub struct BasisUpdateStats {
90 pub updates: u64,
92 pub refactorizations: u64,
94 pub eta_matrices: u64,
96 pub instabilities: u64,
98}
99
100#[derive(Debug)]
102pub struct BasisUpdater {
103 basis: Basis,
105 eta_matrices: Vec<EtaMatrix>,
107 updates_since_refactor: usize,
109 config: BasisUpdateConfig,
111 stats: BasisUpdateStats,
113}
114
115impl BasisUpdater {
116 pub fn new(basis: Basis, config: BasisUpdateConfig) -> Self {
118 Self {
119 basis,
120 eta_matrices: Vec::new(),
121 updates_since_refactor: 0,
122 config,
123 stats: BasisUpdateStats::default(),
124 }
125 }
126
127 pub fn default_config(basis: Basis) -> Self {
129 Self::new(basis, BasisUpdateConfig::default())
130 }
131
132 pub fn pivot(&mut self, entering: VarId, leaving: VarId) {
134 let leaving_pos = self.basis.basic_vars.iter().position(|&v| v == leaving);
136
137 if let Some(pos) = leaving_pos {
138 self.basis.basic_vars[pos] = entering;
140
141 self.basis.nonbasic_vars.retain(|&v| v != entering);
143 self.basis.nonbasic_vars.push(leaving);
144
145 if self.config.use_forrest_tomlin {
147 self.forrest_tomlin_update(pos, entering);
148 }
149
150 self.stats.updates += 1;
151 self.updates_since_refactor += 1;
152
153 if self.updates_since_refactor >= self.config.refactorize_freq {
155 self.refactorize();
156 }
157 }
158 }
159
160 fn forrest_tomlin_update(&mut self, column: usize, _entering: VarId) {
162 let mut eta_vector = FxHashMap::default();
164
165 eta_vector.insert(column, BigRational::one());
167
168 self.eta_matrices.push(EtaMatrix { column, eta_vector });
169
170 self.stats.eta_matrices += 1;
171 }
172
173 fn refactorize(&mut self) {
175 self.eta_matrices.clear();
177 self.updates_since_refactor = 0;
178 self.stats.refactorizations += 1;
179
180 }
182
183 pub fn check_stability(&mut self) -> bool {
185 true
188 }
189
190 pub fn basis(&self) -> &Basis {
192 &self.basis
193 }
194
195 pub fn stats(&self) -> &BasisUpdateStats {
197 &self.stats
198 }
199
200 pub fn reset_stats(&mut self) {
202 self.stats = BasisUpdateStats::default();
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn test_basis_creation() {
212 let basis = Basis::new(vec![0, 1], vec![2, 3]);
213 assert_eq!(basis.size(), 2);
214 assert!(basis.is_basic(0));
215 assert!(!basis.is_basic(2));
216 }
217
218 #[test]
219 fn test_updater_creation() {
220 let basis = Basis::new(vec![0, 1], vec![2, 3]);
221 let updater = BasisUpdater::default_config(basis);
222 assert_eq!(updater.stats().updates, 0);
223 }
224
225 #[test]
226 fn test_pivot() {
227 let basis = Basis::new(vec![0, 1], vec![2, 3]);
228 let mut updater = BasisUpdater::default_config(basis);
229
230 updater.pivot(2, 0);
232
233 assert!(updater.basis().is_basic(2));
234 assert!(!updater.basis().is_basic(0));
235 assert_eq!(updater.stats().updates, 1);
236 }
237
238 #[test]
239 fn test_refactorization() {
240 let basis = Basis::new(vec![0, 1], vec![2, 3]);
241 let config = BasisUpdateConfig {
242 refactorize_freq: 2,
243 ..Default::default()
244 };
245 let mut updater = BasisUpdater::new(basis, config);
246
247 updater.pivot(2, 0);
249 updater.pivot(3, 1);
250 updater.pivot(0, 2);
251
252 assert!(updater.stats().refactorizations > 0);
253 }
254
255 #[test]
256 fn test_eta_matrices() {
257 let basis = Basis::new(vec![0, 1], vec![2, 3]);
258 let mut updater = BasisUpdater::default_config(basis);
259
260 updater.pivot(2, 0);
261
262 assert_eq!(updater.stats().eta_matrices, 1);
263 }
264
265 #[test]
266 fn test_stability_check() {
267 let basis = Basis::new(vec![0, 1], vec![2, 3]);
268 let mut updater = BasisUpdater::default_config(basis);
269
270 assert!(updater.check_stability());
271 }
272
273 #[test]
274 fn test_stats() {
275 let basis = Basis::new(vec![0, 1], vec![2, 3]);
276 let mut updater = BasisUpdater::default_config(basis);
277
278 updater.pivot(2, 0);
279 assert_eq!(updater.stats().updates, 1);
280
281 updater.reset_stats();
282 assert_eq!(updater.stats().updates, 0);
283 }
284}