problemreductions/rules/
minimumcapacitatedspanningtree_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
24use crate::models::graph::MinimumCapacitatedSpanningTree;
25use crate::reduction;
26use crate::rules::traits::{ReduceTo, ReductionResult};
27use crate::topology::{Graph, SimpleGraph};
28use crate::types::WeightElement;
29
30#[derive(Debug, Clone)]
32pub struct ReductionMinimumCapacitatedSpanningTreeToILP {
33 target: ILP<i32>,
34 num_edges: usize,
35}
36
37impl ReductionResult for ReductionMinimumCapacitatedSpanningTreeToILP {
38 type Source = MinimumCapacitatedSpanningTree<SimpleGraph, i32>;
39 type Target = ILP<i32>;
40
41 fn target_problem(&self) -> &ILP<i32> {
42 &self.target
43 }
44
45 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
46 target_solution[..self.num_edges].to_vec()
48 }
49}
50
51#[reduction(
52 overhead = {
53 num_vars = "3 * num_edges",
54 num_constraints = "5 * num_edges + num_vertices + 1",
55 }
56)]
57impl ReduceTo<ILP<i32>> for MinimumCapacitatedSpanningTree<SimpleGraph, i32> {
58 type Result = ReductionMinimumCapacitatedSpanningTreeToILP;
59
60 fn reduce_to(&self) -> Self::Result {
61 let n = self.num_vertices();
62 let m = self.num_edges();
63 let edges = self.graph().edges();
64 let root = self.root();
65 let requirements = self.requirements();
66 let cap = *self.capacity() as f64;
67
68 let num_vars = 3 * m;
69
70 let edge_var = |e: usize| e; let flow_var = |e: usize, dir: usize| m + 2 * e + dir; let total_req: f64 = requirements.iter().map(|r| r.to_sum() as f64).sum();
76
77 let mut constraints = Vec::new();
78
79 let terms: Vec<(usize, f64)> = (0..m).map(|e| (edge_var(e), 1.0)).collect();
81 constraints.push(LinearConstraint::eq(terms, (n - 1) as f64));
82
83 for e in 0..m {
85 constraints.push(LinearConstraint::le(vec![(edge_var(e), 1.0)], 1.0));
86 }
87
88 for (vertex, req) in requirements.iter().enumerate() {
92 let mut terms = Vec::new();
93 for (edge_idx, &(u, v)) in edges.iter().enumerate() {
94 if v == vertex {
97 terms.push((flow_var(edge_idx, 0), 1.0));
99 terms.push((flow_var(edge_idx, 1), -1.0));
101 }
102 if u == vertex {
103 terms.push((flow_var(edge_idx, 0), -1.0));
105 terms.push((flow_var(edge_idx, 1), 1.0));
107 }
108 }
109
110 let rhs = if vertex == root {
111 total_req
113 } else {
114 -(req.to_sum() as f64)
117 };
118 constraints.push(LinearConstraint::eq(terms, rhs));
119 }
120
121 for edge_idx in 0..m {
123 constraints.push(LinearConstraint::le(
124 vec![
125 (flow_var(edge_idx, 0), 1.0),
126 (flow_var(edge_idx, 1), 1.0),
127 (edge_var(edge_idx), -total_req),
128 ],
129 0.0,
130 ));
131 }
132
133 for edge_idx in 0..m {
135 constraints.push(LinearConstraint::le(
136 vec![(flow_var(edge_idx, 0), 1.0)],
137 cap,
138 ));
139 constraints.push(LinearConstraint::le(
140 vec![(flow_var(edge_idx, 1), 1.0)],
141 cap,
142 ));
143 }
144
145 let objective: Vec<(usize, f64)> = self
147 .weights()
148 .iter()
149 .enumerate()
150 .map(|(edge_idx, w)| (edge_var(edge_idx), w.to_sum() as f64))
151 .collect();
152
153 let target = ILP::new(num_vars, constraints, objective, ObjectiveSense::Minimize);
154
155 ReductionMinimumCapacitatedSpanningTreeToILP {
156 target,
157 num_edges: m,
158 }
159 }
160}
161
162#[cfg(feature = "example-db")]
163pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
164 vec![crate::example_db::specs::RuleExampleSpec {
165 id: "minimumcapacitatedspanningtree_to_ilp",
166 build: || {
167 let source = MinimumCapacitatedSpanningTree::new(
168 SimpleGraph::new(4, vec![(0, 1), (0, 2), (1, 2), (1, 3), (2, 3)]),
169 vec![2, 3, 1, 1, 2], 0, vec![0, 1, 1, 1], 2, );
174 crate::example_db::specs::rule_example_via_ilp::<_, i32>(source)
175 },
176 }]
177}
178
179#[cfg(test)]
180#[path = "../unit_tests/rules/minimumcapacitatedspanningtree_ilp.rs"]
181mod tests;