problemreductions/models/graph/
length_bounded_disjoint_paths.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
7use crate::topology::{Graph, SimpleGraph};
8use crate::traits::Problem;
9use crate::types::Max;
10use crate::variant::VariantParam;
11use serde::{Deserialize, Serialize};
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "LengthBoundedDisjointPaths",
16 display_name: "Length-Bounded Disjoint Paths",
17 aliases: &[],
18 dimensions: &[
19 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
20 ],
21 module_path: module_path!(),
22 description: "Maximize the number of internally vertex-disjoint s-t paths of length at most K",
23 fields: &[
24 FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" },
25 FieldInfo { name: "source", type_name: "usize", description: "The shared source vertex s" },
26 FieldInfo { name: "sink", type_name: "usize", description: "The shared sink vertex t" },
27 FieldInfo { name: "max_paths", type_name: "usize", description: "Upper bound on the number of path slots" },
28 FieldInfo { name: "max_length", type_name: "usize", description: "Maximum path length K in edges" },
29 ],
30 }
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
42#[serde(bound(deserialize = "G: serde::Deserialize<'de>"))]
43pub struct LengthBoundedDisjointPaths<G> {
44 graph: G,
45 source: usize,
46 sink: usize,
47 max_paths: usize,
48 max_length: usize,
49}
50
51impl<G: Graph> LengthBoundedDisjointPaths<G> {
52 pub fn new(graph: G, source: usize, sink: usize, max_length: usize) -> Self {
62 assert!(
63 source < graph.num_vertices(),
64 "source must be a valid graph vertex"
65 );
66 assert!(
67 sink < graph.num_vertices(),
68 "sink must be a valid graph vertex"
69 );
70 assert_ne!(source, sink, "source and sink must be distinct");
71 assert!(max_length > 0, "max_length must be positive");
72 let deg_s = graph.neighbors(source).len();
73 let deg_t = graph.neighbors(sink).len();
74 let max_paths = deg_s.min(deg_t);
75 Self {
76 graph,
77 source,
78 sink,
79 max_paths,
80 max_length,
81 }
82 }
83
84 pub fn graph(&self) -> &G {
86 &self.graph
87 }
88
89 pub fn source(&self) -> usize {
91 self.source
92 }
93
94 pub fn sink(&self) -> usize {
96 self.sink
97 }
98
99 pub fn max_paths(&self) -> usize {
101 self.max_paths
102 }
103
104 pub fn max_length(&self) -> usize {
106 self.max_length
107 }
108
109 pub fn num_vertices(&self) -> usize {
111 self.graph.num_vertices()
112 }
113
114 pub fn num_edges(&self) -> usize {
116 self.graph.num_edges()
117 }
118}
119
120impl<G> Problem for LengthBoundedDisjointPaths<G>
121where
122 G: Graph + VariantParam,
123{
124 const NAME: &'static str = "LengthBoundedDisjointPaths";
125 type Value = Max<usize>;
126
127 fn variant() -> Vec<(&'static str, &'static str)> {
128 crate::variant_params![G]
129 }
130
131 fn dims(&self) -> Vec<usize> {
132 vec![2; self.max_paths * self.graph.num_vertices()]
133 }
134
135 fn evaluate(&self, config: &[usize]) -> Max<usize> {
136 validate_path_collection(
137 &self.graph,
138 self.source,
139 self.sink,
140 self.max_paths,
141 self.max_length,
142 config,
143 )
144 }
145}
146
147fn validate_path_collection<G: Graph>(
150 graph: &G,
151 source: usize,
152 sink: usize,
153 max_paths: usize,
154 max_length: usize,
155 config: &[usize],
156) -> Max<usize> {
157 let num_vertices = graph.num_vertices();
158 if config.len() != max_paths * num_vertices {
159 return Max(None);
160 }
161 if config.iter().any(|&value| value > 1) {
162 return Max(None);
163 }
164
165 let mut used_internal = vec![false; num_vertices];
166 let mut used_direct_path = false;
167 let mut count = 0usize;
168 for slot in config.chunks(num_vertices) {
169 if slot.iter().all(|&v| v == 0) {
171 continue;
172 }
173 if !is_valid_path_slot(
174 graph,
175 source,
176 sink,
177 max_length,
178 slot,
179 &mut used_internal,
180 &mut used_direct_path,
181 ) {
182 return Max(None);
183 }
184 count += 1;
185 }
186 Max(Some(count))
187}
188
189fn is_valid_path_slot<G: Graph>(
190 graph: &G,
191 source: usize,
192 sink: usize,
193 max_length: usize,
194 slot: &[usize],
195 used_internal: &mut [bool],
196 used_direct_path: &mut bool,
197) -> bool {
198 if slot.len() != graph.num_vertices()
199 || slot.get(source) != Some(&1)
200 || slot.get(sink) != Some(&1)
201 {
202 return false;
203 }
204
205 let selected = slot
206 .iter()
207 .enumerate()
208 .filter_map(|(vertex, &chosen)| (chosen == 1).then_some(vertex))
209 .collect::<Vec<_>>();
210 if selected.len() < 2 {
211 return false;
212 }
213
214 let mut in_path = vec![false; graph.num_vertices()];
215 for &vertex in &selected {
216 in_path[vertex] = true;
217 if vertex != source && vertex != sink && used_internal[vertex] {
218 return false;
219 }
220 }
221
222 let mut degree_sum = 0usize;
223 for &vertex in &selected {
224 let degree = graph
225 .neighbors(vertex)
226 .into_iter()
227 .filter(|&neighbor| in_path[neighbor])
228 .count();
229 degree_sum += degree;
230
231 if vertex == source || vertex == sink {
232 if degree != 1 {
233 return false;
234 }
235 } else if degree != 2 {
236 return false;
237 }
238 }
239
240 let edge_count = degree_sum / 2;
241 if edge_count + 1 != selected.len() || edge_count > max_length {
242 return false;
243 }
244 if edge_count == 1 {
245 if *used_direct_path {
246 return false;
247 }
248 *used_direct_path = true;
249 }
250
251 let mut seen = vec![false; graph.num_vertices()];
252 let mut stack = vec![source];
253 seen[source] = true;
254 let mut seen_count = 0usize;
255 while let Some(vertex) = stack.pop() {
256 seen_count += 1;
257 for neighbor in graph.neighbors(vertex) {
258 if in_path[neighbor] && !seen[neighbor] {
259 seen[neighbor] = true;
260 stack.push(neighbor);
261 }
262 }
263 }
264
265 if !seen[sink] || seen_count != selected.len() {
266 return false;
267 }
268
269 for &vertex in &selected {
270 if vertex != source && vertex != sink {
271 used_internal[vertex] = true;
272 }
273 }
274 true
275}
276
277#[cfg(feature = "example-db")]
278fn encode_paths(num_vertices: usize, max_paths: usize, slots: &[&[usize]]) -> Vec<usize> {
279 let mut config = vec![0; num_vertices * max_paths];
280 for (slot_index, slot_vertices) in slots.iter().enumerate() {
281 let offset = slot_index * num_vertices;
282 for &vertex in *slot_vertices {
283 config[offset + vertex] = 1;
284 }
285 }
286 config
287}
288
289#[cfg(feature = "example-db")]
290pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
291 let graph = SimpleGraph::new(5, vec![(0, 1), (1, 4), (0, 2), (2, 4), (0, 3), (3, 4)]);
292 vec![crate::example_db::specs::ModelExampleSpec {
296 id: "length_bounded_disjoint_paths_simplegraph",
297 instance: Box::new(LengthBoundedDisjointPaths::new(graph, 0, 4, 3)),
298 optimal_config: encode_paths(5, 3, &[&[0, 1, 4], &[0, 2, 4], &[0, 3, 4]]),
299 optimal_value: serde_json::json!(3),
300 }]
301}
302
303crate::declare_variants! {
304 default LengthBoundedDisjointPaths<SimpleGraph> => "2^(max_paths * num_vertices)",
305}
306
307#[cfg(test)]
308#[path = "../../unit_tests/models/graph/length_bounded_disjoint_paths.rs"]
309mod tests;