pub struct SquareComponents {
pub components: Vec<SquareComponent>,
}Expand description
Decomposition of the square sub-graph into connected components.
Fields§
§components: Vec<SquareComponent>One entry per component, sorted by smallest contained equality-row index for determinism.
Implementations§
Source§impl SquareComponents
impl SquareComponents
Sourcepub fn of_square_part(
inc: &EqualityIncidence,
_m: &BipartiteMatching,
part: &DulmageMendelsohnPartition,
) -> Self
pub fn of_square_part( inc: &EqualityIncidence, _m: &BipartiteMatching, part: &DulmageMendelsohnPartition, ) -> Self
Decompose the square sub-graph of part into connected
components. Edges considered are exactly those of inc whose
row AND column are both in DMPart::Square.
§Example
use pounce_presolve::incidence::{EqualityIncidence, ProbeView};
use pounce_presolve::matching::hopcroft_karp;
use pounce_presolve::dulmage_mendelsohn::DulmageMendelsohnPartition;
use pounce_presolve::components::SquareComponents;
// 4×4 with a 2-block and two singletons.
let p = ProbeView {
n_vars: 4,
m_rows: 4,
jac_irow: &[0, 0, 1, 1, 2, 3],
jac_jcol: &[0, 1, 0, 1, 2, 3],
jac_values: None,
g_l: &[0.0; 4],
g_u: &[0.0; 4],
linearity: None,
one_based: false,
eq_tol: 1e-12,
excluded_vars: None,
excluded_rows: None,
};
let inc = EqualityIncidence::from_probe(&p);
let m = hopcroft_karp(&inc);
let dm = DulmageMendelsohnPartition::from_matching(&inc, &m);
let c = SquareComponents::of_square_part(&inc, &m, &dm);
assert_eq!(c.components.len(), 3);Examples found in repository?
examples/dm_partition.rs (line 60)
28fn main() {
29 let irow: [i32; 9] = [0, 1, 1, 2, 2, 3, 3, 4, 4];
30 let jcol: [i32; 9] = [0, 1, 2, 1, 2, 3, 4, 3, 4];
31 let g = [0.0; 5];
32
33 let probe = ProbeView {
34 n_vars: 5,
35 m_rows: 5,
36 jac_irow: &irow,
37 jac_jcol: &jcol,
38 jac_values: None,
39 g_l: &g,
40 g_u: &g,
41 linearity: None,
42 one_based: false,
43 eq_tol: 1e-12,
44 excluded_vars: None,
45 excluded_rows: None,
46 };
47 let inc = EqualityIncidence::from_probe(&probe);
48 let m = hopcroft_karp(&inc);
49 let dm = DulmageMendelsohnPartition::from_matching(&inc, &m);
50
51 println!("dm_partition example — PR 3 of pounce#53");
52 println!("matching size: {}", m.size);
53 println!("over rows: {:?}, cols: {:?}", dm.over_rows, dm.over_cols);
54 println!(
55 "square rows: {:?}, cols: {:?}",
56 dm.square_rows, dm.square_cols
57 );
58 println!("under rows: {:?}, cols: {:?}", dm.under_rows, dm.under_cols);
59
60 let comps = SquareComponents::of_square_part(&inc, &m, &dm);
61 println!("square components: {}", comps.components.len());
62 for (i, c) in comps.components.iter().enumerate() {
63 println!(" component {i}: rows={:?}, cols={:?}", c.eq_rows, c.cols);
64 }
65
66 assert_eq!(comps.components.len(), 3, "expected 3 independent blocks");
67}More examples
examples/btf_chain.rs (line 51)
29fn main() {
30 let irow: [i32; 7] = [0, 1, 1, 2, 2, 3, 3];
31 let jcol: [i32; 7] = [0, 0, 1, 1, 2, 2, 3];
32 let g = [0.0; 4];
33
34 let probe = ProbeView {
35 n_vars: 4,
36 m_rows: 4,
37 jac_irow: &irow,
38 jac_jcol: &jcol,
39 jac_values: None,
40 g_l: &g,
41 g_u: &g,
42 linearity: None,
43 one_based: false,
44 eq_tol: 1e-12,
45 excluded_vars: None,
46 excluded_rows: None,
47 };
48 let inc = EqualityIncidence::from_probe(&probe);
49 let m = hopcroft_karp(&inc);
50 let dm = DulmageMendelsohnPartition::from_matching(&inc, &m);
51 let comps = SquareComponents::of_square_part(&inc, &m, &dm);
52
53 println!("btf_chain example — PR 4 of pounce#53");
54 println!("matching size: {}", m.size);
55 println!("square components: {}", comps.components.len());
56 assert_eq!(
57 comps.components.len(),
58 1,
59 "expected one connected component"
60 );
61
62 let btf = BlockTriangularForm::of_component(&inc, &m, &comps.components[0]);
63 println!("BTF blocks (elimination order):");
64 for (i, b) in btf.blocks.iter().enumerate() {
65 println!(" block {i}: rows={:?}, cols={:?}", b.eq_rows, b.cols);
66 }
67 assert_eq!(btf.blocks.len(), 4, "chain expands to 4 size-1 blocks");
68 for (i, b) in btf.blocks.iter().enumerate() {
69 assert_eq!(b.eq_rows, vec![i]);
70 assert_eq!(b.cols, vec![i]);
71 }
72}examples/pipeline_demo.rs (line 129)
68fn main() {
69 let n_vars = 4; // a, b, c, d
70 let n_rows = 4;
71 // Jacobian sparsity (row-major).
72 let jac_pattern = [
73 (0, 0), // c0: a
74 (1, 1),
75 (1, 2), // c1: b + c
76 (2, 1),
77 (2, 2), // c2: b - c
78 (3, 0),
79 (3, 1),
80 (3, 3), // c3: a + b + d
81 ];
82 let jac_irow: Vec<i32> = jac_pattern.iter().map(|(r, _)| *r as i32).collect();
83 let jac_jcol: Vec<i32> = jac_pattern.iter().map(|(_, c)| *c as i32).collect();
84 let g_l = [1.0, 5.0, -1.0, 4.0];
85 let g_u = [1.0, 5.0, -1.0, 4.0]; // all equalities
86
87 let probe = ProbeView {
88 n_vars,
89 m_rows: n_rows,
90 jac_irow: &jac_irow,
91 jac_jcol: &jac_jcol,
92 jac_values: None,
93 g_l: &g_l,
94 g_u: &g_u,
95 linearity: None,
96 one_based: false,
97 eq_tol: 1e-12,
98 excluded_vars: None,
99 excluded_rows: None,
100 };
101
102 println!("pipeline_demo — PRs 2-7 of pounce#53");
103 println!("======================================");
104
105 // ----- Stage 1: incidence + matching -----
106 let inc = EqualityIncidence::from_probe(&probe);
107 println!(
108 "\n[Stage 1] EqualityIncidence: {} eq rows, {} vars",
109 inc.n_eq_rows(),
110 inc.n_vars
111 );
112 let m = hopcroft_karp(&inc);
113 println!("[Stage 1] Matching: size = {}", m.size);
114
115 // ----- Stage 2: DM partition -----
116 let dm = DulmageMendelsohnPartition::from_matching(&inc, &m);
117 println!("\n[Stage 2] DM partition:");
118 println!(" over rows={:?}, cols={:?}", dm.over_rows, dm.over_cols);
119 println!(
120 " square rows={:?}, cols={:?}",
121 dm.square_rows, dm.square_cols
122 );
123 println!(
124 " under rows={:?}, cols={:?}",
125 dm.under_rows, dm.under_cols
126 );
127
128 // ----- Stage 3: components -----
129 let comps = SquareComponents::of_square_part(&inc, &m, &dm);
130 println!(
131 "\n[Stage 3] Components: {} square block(s)",
132 comps.components.len()
133 );
134 for (i, c) in comps.components.iter().enumerate() {
135 println!(" component {i}: rows={:?}, cols={:?}", c.eq_rows, c.cols);
136 }
137
138 // ----- Stage 4: BTF -----
139 println!("\n[Stage 4] BTF (per component):");
140 let ineq = InequalityIncidence::from_probe(&probe);
141
142 // Walk each component's BTF, solve each block, build the frame.
143 let mut all_fixed_vars: Vec<usize> = Vec::new();
144 let mut all_fixed_values: Vec<Number> = Vec::new();
145 let mut all_dropped_rows: Vec<usize> = Vec::new();
146 let mut x_running = vec![0.0; n_vars];
147 // Full A and b for this problem, used both for block extraction
148 // and the final KKT check.
149 let a_full = [
150 [1.0, 0.0, 0.0, 0.0],
151 [0.0, 1.0, 1.0, 0.0],
152 [0.0, 1.0, -1.0, 0.0],
153 [1.0, 1.0, 0.0, 1.0],
154 ];
155 let b_full = [1.0, 5.0, -1.0, 4.0];
156
157 for comp in &comps.components {
158 let btf = BlockTriangularForm::of_component(&inc, &m, comp);
159 for (bi, block) in btf.blocks.iter().enumerate() {
160 let class = classify_block(block, &ineq, &Default::default());
161 println!(
162 " component cols={:?}, block {bi}: rows={:?}, cols={:?}, class={class:?}",
163 comp.cols, block.eq_rows, block.cols
164 );
165 assert_eq!(class, AuxiliaryCouplingClass::PureEquality);
166
167 // Build a small linear system for this block, splicing in
168 // the values of already-solved variables.
169 let k = block.eq_rows.len();
170 let mut a_block = vec![0.0; k * k];
171 let mut b_block = vec![0.0; k];
172 for (ii, &r) in block.eq_rows.iter().enumerate() {
173 let mut rhs = b_full[r];
174 for j in 0..n_vars {
175 if let Some(jj) = block.cols.iter().position(|&c| c == j) {
176 a_block[ii * k + jj] = a_full[r][j];
177 } else {
178 rhs -= a_full[r][j] * x_running[j];
179 }
180 }
181 b_block[ii] = rhs;
182 }
183
184 // Solve.
185 let mut eqs = LinearBlockEqs {
186 a: a_block,
187 b: b_block,
188 n: k,
189 };
190 let mut solver = DampedNewtonSolver;
191 let opt = BlockSolveOptions::default();
192 let out = solver.solve(&vec![0.0; k], &mut eqs, &opt).unwrap();
193
194 // Record the block's solution.
195 for (ii, &c) in block.cols.iter().enumerate() {
196 x_running[c] = out.x[ii];
197 all_fixed_vars.push(c);
198 all_fixed_values.push(out.x[ii]);
199 }
200 for &r in &block.eq_rows {
201 all_dropped_rows.push(r);
202 }
203 }
204 }
205
206 // ----- Stage 5: lift via the reduction frame -----
207 println!("\n[Stage 5] After block solves:");
208 println!(" x_running (a, b, c, d) = {:?}", x_running);
209 // For this demo we declare ALL fixed vars and rows as one
210 // composite frame — in the real PR 8 orchestrator, each
211 // elimination layer becomes its own frame on the stack.
212 // Sort by variable index for the frame's contract.
213 let mut order: Vec<usize> = (0..all_fixed_vars.len()).collect();
214 order.sort_by_key(|&i| all_fixed_vars[i]);
215 let fixed_vars_sorted: Vec<usize> = order.iter().map(|&i| all_fixed_vars[i]).collect();
216 let fixed_values_sorted: Vec<Number> = order.iter().map(|&i| all_fixed_values[i]).collect();
217 let mut dropped_rows_sorted = all_dropped_rows.clone();
218 dropped_rows_sorted.sort_unstable();
219
220 let frame = ReductionFrame::new(
221 n_vars,
222 n_rows,
223 fixed_vars_sorted.clone(),
224 fixed_values_sorted,
225 dropped_rows_sorted.clone(),
226 );
227 println!(
228 " ReductionFrame: fixed_vars={:?}, dropped_rows={:?}",
229 frame.fixed_vars, frame.dropped_rows
230 );
231 println!(
232 " reduced shape: {} vars × {} rows",
233 frame.n_reduced_vars(),
234 frame.n_reduced_rows()
235 );
236
237 // For this problem BTF decomposed every row into a block — the
238 // reduced shape is 0×0 and no IPM step is needed. The lift is
239 // trivial: x_full = the values the block solves produced.
240 let x_reduced: Vec<Number> = Vec::new();
241 println!("\n[Stage 6] Reduced problem is empty (presolve solved it all).");
242 let x_full = frame.lift_x(&x_reduced);
243 println!(" lifted x_full = {:?}", x_full);
244
245 // Sanity: should match (1, 2, 3, 1).
246 let expected = [1.0, 2.0, 3.0, 1.0];
247 let max_x_err = x_full
248 .iter()
249 .zip(expected.iter())
250 .map(|(a, b)| (a - b).abs())
251 .fold(0.0_f64, f64::max);
252 println!(" max |x - expected|: {:.3e}", max_x_err);
253 assert!(max_x_err < 1e-9);
254
255 // ----- Stage 7: multiplier recovery -----
256 // Objective: f = (a-1)² + (b-2)² + (c-3)² + d²
257 // ∇f at the optimum (1, 2, 3, 1): (0, 0, 0, 2).
258 let grad_f = [
259 2.0 * (x_full[0] - 1.0),
260 2.0 * (x_full[1] - 2.0),
261 2.0 * (x_full[2] - 3.0),
262 2.0 * x_full[3],
263 ];
264 println!("\n[Stage 7] ∇f at the optimum: {:?}", grad_f);
265
266 // No kept rows → reduced λ is empty.
267 let lambda_reduced: Vec<Number> = Vec::new();
268 let mut lambda_full = frame.lift_lambda(&lambda_reduced);
269 println!(" reduced λ = {:?}", lambda_reduced);
270 println!(" lifted λ (pre-recovery) = {:?}", lambda_full);
271
272 // Build full Jacobian for the recovery LU.
273 let mut jac_full = vec![0.0; n_rows * n_vars];
274 for (r, row) in a_full.iter().enumerate() {
275 for (c, &v) in row.iter().enumerate() {
276 jac_full[r * n_vars + c] = v;
277 }
278 }
279 let lam_dropped = frame
280 .recover_dropped_multipliers(&grad_f, &jac_full, &lambda_full)
281 .unwrap();
282 for (idx, &r) in frame.dropped_rows.iter().enumerate() {
283 lambda_full[r] = lam_dropped[idx];
284 }
285 println!(" recovered λ (full) = {:?}", lambda_full);
286
287 // Final KKT residual at every variable.
288 println!("\n[Stage 8] Full-space KKT residual:");
289 let mut max_kkt: Number = 0.0;
290 for i in 0..n_vars {
291 let mut s = grad_f[i];
292 for r in 0..n_rows {
293 s -= jac_full[r * n_vars + i] * lambda_full[r];
294 }
295 println!(" ∂L/∂x[{i}] = {:.3e}", s);
296 max_kkt = max_kkt.max(s.abs());
297 }
298 println!(" max |KKT residual| = {:.3e}", max_kkt);
299 assert!(max_kkt < 1e-10);
300 println!("\n✓ Pipeline end-to-end correct.");
301}Trait Implementations§
Source§impl Clone for SquareComponents
impl Clone for SquareComponents
Source§fn clone(&self) -> SquareComponents
fn clone(&self) -> SquareComponents
Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreSource§impl Debug for SquareComponents
impl Debug for SquareComponents
Source§impl Default for SquareComponents
impl Default for SquareComponents
Source§fn default() -> SquareComponents
fn default() -> SquareComponents
Returns the “default value” for a type. Read more
Auto Trait Implementations§
impl Freeze for SquareComponents
impl RefUnwindSafe for SquareComponents
impl Send for SquareComponents
impl Sync for SquareComponents
impl Unpin for SquareComponents
impl UnsafeUnpin for SquareComponents
impl UnwindSafe for SquareComponents
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more