1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
7use crate::models::graph::LengthBoundedDisjointPaths;
8use crate::reduction;
9use crate::rules::traits::{ReduceTo, ReductionResult};
10use crate::topology::{Graph, SimpleGraph};
11
12#[derive(Debug, Clone)]
19pub struct ReductionLBDPToILP {
20 target: ILP<bool>,
21 edges: Vec<(usize, usize)>,
23 num_vertices: usize,
24 num_paths: usize,
25}
26
27impl ReductionResult for ReductionLBDPToILP {
28 type Source = LengthBoundedDisjointPaths<SimpleGraph>;
29 type Target = ILP<bool>;
30
31 fn target_problem(&self) -> &ILP<bool> {
32 &self.target
33 }
34
35 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
36 let m = self.edges.len();
39 let n = self.num_vertices;
40 let j = self.num_paths;
41 let flow_vars_per_k = 2 * m;
42
43 let mut result = vec![0usize; j * n];
44 for k in 0..j {
45 let mut on_path = vec![false; n];
47 for e in 0..m {
48 let (u, v) = self.edges[e];
49 let fwd = target_solution[k * flow_vars_per_k + 2 * e];
50 let rev = target_solution[k * flow_vars_per_k + 2 * e + 1];
51 if fwd == 1 {
52 on_path[u] = true;
53 on_path[v] = true;
54 }
55 if rev == 1 {
56 on_path[u] = true;
57 on_path[v] = true;
58 }
59 }
60 for v in 0..n {
61 if on_path[v] {
62 result[k * n + v] = 1;
63 }
64 }
65 }
66 result
67 }
68}
69
70#[reduction(
71 overhead = {
72 num_vars = "max_paths * 2 * num_edges + max_paths",
73 num_constraints = "max_paths * num_vertices + max_paths * num_edges + max_paths + num_edges + num_vertices + max_paths",
74 }
75)]
76impl ReduceTo<ILP<bool>> for LengthBoundedDisjointPaths<SimpleGraph> {
77 type Result = ReductionLBDPToILP;
78
79 #[allow(clippy::needless_range_loop)]
80 fn reduce_to(&self) -> Self::Result {
81 let mut edges: Vec<(usize, usize)> = self
82 .graph()
83 .edges()
84 .into_iter()
85 .map(|(u, v)| if u <= v { (u, v) } else { (v, u) })
86 .collect();
87 edges.sort_unstable();
88
89 let m = edges.len();
90 let n = self.num_vertices();
91 let j = self.max_paths();
92 let max_len = self.max_length();
93 let s = self.source();
94 let t = self.sink();
95
96 let flow_vars_per_k = 2 * m;
98 let num_flow = j * flow_vars_per_k;
99 let a_var = |k: usize| num_flow + k;
100 let num_vars = num_flow + j;
101
102 let flow_var = |k: usize, e: usize, dir: usize| k * flow_vars_per_k + 2 * e + dir;
103
104 let mut vertex_edges: Vec<Vec<usize>> = vec![Vec::new(); n];
106 for (e, &(u, v)) in edges.iter().enumerate() {
107 vertex_edges[u].push(e);
108 vertex_edges[v].push(e);
109 }
110
111 let mut constraints = Vec::new();
112
113 for k in 0..j {
114 for vertex in 0..n {
116 let mut terms = Vec::new();
117 for &e in &vertex_edges[vertex] {
118 let (eu, _) = edges[e];
119 if vertex == eu {
120 terms.push((flow_var(k, e, 0), 1.0)); terms.push((flow_var(k, e, 1), -1.0)); } else {
123 terms.push((flow_var(k, e, 1), 1.0)); terms.push((flow_var(k, e, 0), -1.0)); }
126 }
127 if vertex == s {
128 terms.push((a_var(k), -1.0));
130 constraints.push(LinearConstraint::eq(terms, 0.0));
131 } else if vertex == t {
132 terms.push((a_var(k), 1.0));
134 constraints.push(LinearConstraint::eq(terms, 0.0));
135 } else {
136 constraints.push(LinearConstraint::eq(terms, 0.0));
137 }
138 }
139
140 for e in 0..m {
142 constraints.push(LinearConstraint::le(
143 vec![(flow_var(k, e, 0), 1.0), (flow_var(k, e, 1), 1.0)],
144 1.0,
145 ));
146 }
147
148 let mut len_terms = Vec::new();
150 for e in 0..m {
151 len_terms.push((flow_var(k, e, 0), 1.0));
152 len_terms.push((flow_var(k, e, 1), 1.0));
153 }
154 len_terms.push((a_var(k), -(max_len as f64)));
155 constraints.push(LinearConstraint::le(len_terms, 0.0));
156 }
157
158 for e in 0..m {
160 let mut terms = Vec::new();
161 for k in 0..j {
162 terms.push((flow_var(k, e, 0), 1.0));
163 terms.push((flow_var(k, e, 1), 1.0));
164 }
165 constraints.push(LinearConstraint::le(terms, 1.0));
166 }
167
168 for v in 0..n {
170 if v == s || v == t {
171 continue;
172 }
173 let mut terms = Vec::new();
174 for k in 0..j {
175 for &e in &vertex_edges[v] {
176 let (eu, _) = edges[e];
177 if v == eu {
178 terms.push((flow_var(k, e, 0), 1.0));
179 } else {
180 terms.push((flow_var(k, e, 1), 1.0));
181 }
182 }
183 }
184 constraints.push(LinearConstraint::le(terms, 1.0));
185 }
186
187 let objective: Vec<(usize, f64)> = (0..j).map(|k| (a_var(k), 1.0)).collect();
189 let target = ILP::new(num_vars, constraints, objective, ObjectiveSense::Maximize);
190
191 ReductionLBDPToILP {
192 target,
193 edges,
194 num_vertices: n,
195 num_paths: j,
196 }
197 }
198}
199
200#[cfg(feature = "example-db")]
201pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
202 vec![crate::example_db::specs::RuleExampleSpec {
203 id: "lengthboundeddisjointpaths_to_ilp",
204 build: || {
205 let source = LengthBoundedDisjointPaths::new(
207 SimpleGraph::new(4, vec![(0, 1), (0, 2), (1, 3), (2, 3)]),
208 0,
209 3,
210 2,
211 );
212 crate::example_db::specs::rule_example_via_ilp::<_, bool>(source)
213 },
214 }]
215}
216
217#[cfg(test)]
218#[path = "../unit_tests/rules/lengthboundeddisjointpaths_ilp.rs"]
219mod tests;