problemreductions/rules/
maximumleafspanningtree_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
22use crate::models::graph::MaximumLeafSpanningTree;
23use crate::reduction;
24use crate::rules::traits::{ReduceTo, ReductionResult};
25use crate::topology::{Graph, SimpleGraph};
26
27#[derive(Debug, Clone)]
29pub struct ReductionMaximumLeafSpanningTreeToILP {
30 target: ILP<i32>,
31 num_edges: usize,
32}
33
34impl ReductionResult for ReductionMaximumLeafSpanningTreeToILP {
35 type Source = MaximumLeafSpanningTree<SimpleGraph>;
36 type Target = ILP<i32>;
37
38 fn target_problem(&self) -> &ILP<i32> {
39 &self.target
40 }
41
42 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
43 target_solution[..self.num_edges].to_vec()
45 }
46}
47
48#[reduction(
49 overhead = {
50 num_vars = "3 * num_edges + num_vertices",
51 num_constraints = "3 * num_vertices + 2 * num_edges + 1",
52 }
53)]
54impl ReduceTo<ILP<i32>> for MaximumLeafSpanningTree<SimpleGraph> {
55 type Result = ReductionMaximumLeafSpanningTreeToILP;
56
57 fn reduce_to(&self) -> Self::Result {
58 let n = self.num_vertices();
59 let m = self.num_edges();
60 let edges = self.graph().edges();
61 let root = 0usize;
62
63 let num_vars = 3 * m + n;
64 let edge_var = |e: usize| e; let leaf_var = |v: usize| m + v; let flow_var = |e: usize, dir: usize| m + n + 2 * e + dir; let mut constraints = Vec::new();
70
71 let terms: Vec<(usize, f64)> = (0..m).map(|e| (edge_var(e), 1.0)).collect();
73 constraints.push(LinearConstraint::eq(terms, (n - 1) as f64));
74
75 for vertex in 0..n {
78 let mut terms = Vec::new();
79 for (edge_idx, &(u, v)) in edges.iter().enumerate() {
80 if v == vertex {
83 terms.push((flow_var(edge_idx, 0), 1.0));
85 terms.push((flow_var(edge_idx, 1), -1.0));
87 }
88 if u == vertex {
89 terms.push((flow_var(edge_idx, 0), -1.0));
91 terms.push((flow_var(edge_idx, 1), 1.0));
93 }
94 }
95
96 let rhs = if vertex == root {
97 -((n - 1) as f64)
99 } else {
100 1.0
102 };
103 constraints.push(LinearConstraint::eq(terms, rhs));
104 }
105
106 for edge_idx in 0..m {
108 constraints.push(LinearConstraint::le(
109 vec![
110 (flow_var(edge_idx, 0), 1.0),
111 (flow_var(edge_idx, 1), 1.0),
112 (edge_var(edge_idx), -((n - 1) as f64)),
113 ],
114 0.0,
115 ));
116 }
117
118 let mut incident: Vec<Vec<usize>> = vec![vec![]; n];
123 for (edge_idx, &(u, v)) in edges.iter().enumerate() {
124 incident[u].push(edge_idx);
125 incident[v].push(edge_idx);
126 }
127
128 for (v, inc) in incident.iter().enumerate() {
129 let mut terms: Vec<(usize, f64)> = inc.iter().map(|&e| (edge_var(e), 1.0)).collect();
130 terms.push((leaf_var(v), (n - 2) as f64));
131 constraints.push(LinearConstraint::le(terms, (n - 1) as f64));
132 }
133
134 for e in 0..m {
136 constraints.push(LinearConstraint::le(vec![(edge_var(e), 1.0)], 1.0));
137 }
138 for v in 0..n {
139 constraints.push(LinearConstraint::le(vec![(leaf_var(v), 1.0)], 1.0));
140 }
141
142 let objective: Vec<(usize, f64)> = (0..n).map(|v| (leaf_var(v), 1.0)).collect();
144
145 let target = ILP::new(num_vars, constraints, objective, ObjectiveSense::Maximize);
146
147 ReductionMaximumLeafSpanningTreeToILP {
148 target,
149 num_edges: m,
150 }
151 }
152}
153
154#[cfg(feature = "example-db")]
155pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
156 vec![crate::example_db::specs::RuleExampleSpec {
157 id: "maximumleafspanningtree_to_ilp",
158 build: || {
159 let source = MaximumLeafSpanningTree::new(SimpleGraph::new(
160 4,
161 vec![(0, 1), (1, 2), (2, 3), (0, 2)],
162 ));
163 crate::example_db::specs::rule_example_via_ilp::<_, i32>(source)
164 },
165 }]
166}
167
168#[cfg(test)]
169#[path = "../unit_tests/rules/maximumleafspanningtree_ilp.rs"]
170mod tests;