problemreductions/rules/
optimumcommunicationspanningtree_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
11use crate::models::misc::OptimumCommunicationSpanningTree;
12use crate::reduction;
13use crate::rules::traits::{ReduceTo, ReductionResult};
14
15#[derive(Debug, Clone)]
23pub struct ReductionOptimumCommunicationSpanningTreeToILP {
24 target: ILP<bool>,
25 num_edges: usize,
26}
27
28impl ReductionResult for ReductionOptimumCommunicationSpanningTreeToILP {
29 type Source = OptimumCommunicationSpanningTree;
30 type Target = ILP<bool>;
31
32 fn target_problem(&self) -> &ILP<bool> {
33 &self.target
34 }
35
36 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
37 target_solution[..self.num_edges].to_vec()
38 }
39}
40
41#[reduction(
42 overhead = {
43 num_vars = "num_edges + 2 * num_edges * num_vertices * (num_vertices - 1) / 2",
44 num_constraints = "1 + num_vertices * num_vertices * (num_vertices - 1) / 2 + 2 * num_edges * num_vertices * (num_vertices - 1) / 2",
45 }
46)]
47impl ReduceTo<ILP<bool>> for OptimumCommunicationSpanningTree {
48 type Result = ReductionOptimumCommunicationSpanningTreeToILP;
49
50 fn reduce_to(&self) -> Self::Result {
51 let n = self.num_vertices();
52 let m = self.num_edges();
53 let edges = self.edges();
54 let w = self.edge_weights();
55 let r = self.requirements();
56
57 let mut commodities: Vec<(usize, usize)> = Vec::new();
59 for (s, row) in r.iter().enumerate() {
60 for (t, &req) in row.iter().enumerate().skip(s + 1) {
61 if req > 0 {
62 commodities.push((s, t));
63 }
64 }
65 }
66 let num_commodities = commodities.len();
67
68 let num_vars = m + 2 * m * num_commodities;
69 let mut constraints = Vec::new();
70
71 let edge_var = |edge_idx: usize| edge_idx;
73
74 let flow_var =
76 |k: usize, edge_idx: usize, dir: usize| -> usize { m + k * 2 * m + 2 * edge_idx + dir };
77
78 let tree_terms: Vec<(usize, f64)> = (0..m).map(|e| (edge_var(e), 1.0)).collect();
81 constraints.push(LinearConstraint::eq(tree_terms, (n - 1) as f64));
82
83 for (k, &(src, dst)) in commodities.iter().enumerate() {
85 for vertex in 0..n {
86 let mut terms = Vec::new();
87 for (edge_idx, &(i, j)) in edges.iter().enumerate() {
88 if j == vertex {
90 terms.push((flow_var(k, edge_idx, 0), 1.0));
92 terms.push((flow_var(k, edge_idx, 1), -1.0));
93 }
94 if i == vertex {
95 terms.push((flow_var(k, edge_idx, 1), 1.0));
97 terms.push((flow_var(k, edge_idx, 0), -1.0));
98 }
99 }
100
101 let rhs = if vertex == src {
102 -1.0 } else if vertex == dst {
104 1.0 } else {
106 0.0 };
108 constraints.push(LinearConstraint::eq(terms, rhs));
109 }
110 }
111
112 for k in 0..num_commodities {
114 for edge_idx in 0..m {
115 let sel = edge_var(edge_idx);
116 constraints.push(LinearConstraint::le(
118 vec![(flow_var(k, edge_idx, 0), 1.0), (sel, -1.0)],
119 0.0,
120 ));
121 constraints.push(LinearConstraint::le(
123 vec![(flow_var(k, edge_idx, 1), 1.0), (sel, -1.0)],
124 0.0,
125 ));
126 }
127 }
128
129 let mut objective: Vec<(usize, f64)> = Vec::new();
132 for (k, &(s, t)) in commodities.iter().enumerate() {
133 let req = r[s][t] as f64;
134 for (edge_idx, &(i, j)) in edges.iter().enumerate() {
135 let weight = w[i][j] as f64;
136 let coeff = req * weight;
137 if coeff != 0.0 {
138 objective.push((flow_var(k, edge_idx, 0), coeff));
139 objective.push((flow_var(k, edge_idx, 1), coeff));
140 }
141 }
142 }
143
144 let target = ILP::new(num_vars, constraints, objective, ObjectiveSense::Minimize);
145
146 ReductionOptimumCommunicationSpanningTreeToILP {
147 target,
148 num_edges: m,
149 }
150 }
151}
152
153#[cfg(feature = "example-db")]
154pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
155 vec![crate::example_db::specs::RuleExampleSpec {
156 id: "optimum_communication_spanning_tree_to_ilp",
157 build: || {
158 let edge_weights = vec![vec![0, 1, 2], vec![1, 0, 3], vec![2, 3, 0]];
160 let requirements = vec![vec![0, 1, 1], vec![1, 0, 1], vec![1, 1, 0]];
161 let source = OptimumCommunicationSpanningTree::new(edge_weights, requirements);
162 crate::example_db::specs::rule_example_via_ilp::<_, bool>(source)
163 },
164 }]
165}
166
167#[cfg(test)]
168#[path = "../unit_tests/rules/optimumcommunicationspanningtree_ilp.rs"]
169mod tests;