problemreductions/models/graph/
disjoint_connecting_paths.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
7use crate::topology::{Graph, SimpleGraph};
8use crate::traits::Problem;
9use crate::variant::VariantParam;
10use serde::{Deserialize, Serialize};
11use std::collections::BTreeSet;
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "DisjointConnectingPaths",
16 display_name: "Disjoint Connecting Paths",
17 aliases: &[],
18 dimensions: &[
19 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
20 ],
21 module_path: module_path!(),
22 description: "Find pairwise vertex-disjoint paths connecting given terminal pairs",
23 fields: &[
24 FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" },
25 FieldInfo { name: "terminal_pairs", type_name: "Vec<(usize, usize)>", description: "Disjoint terminal pairs (s_i, t_i)" },
26 ],
27 }
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
36#[serde(bound(deserialize = "G: serde::Deserialize<'de>"))]
37pub struct DisjointConnectingPaths<G> {
38 graph: G,
39 terminal_pairs: Vec<(usize, usize)>,
40}
41
42impl<G: Graph> DisjointConnectingPaths<G> {
43 pub fn new(graph: G, terminal_pairs: Vec<(usize, usize)>) -> Self {
50 assert!(
51 !terminal_pairs.is_empty(),
52 "terminal_pairs must contain at least one pair"
53 );
54
55 let num_vertices = graph.num_vertices();
56 let mut used = vec![false; num_vertices];
57 for &(source, sink) in &terminal_pairs {
58 assert!(source < num_vertices, "terminal pair source out of bounds");
59 assert!(sink < num_vertices, "terminal pair sink out of bounds");
60 assert_ne!(source, sink, "terminal pair endpoints must be distinct");
61 assert!(
62 !used[source],
63 "terminal vertices must be pairwise disjoint across pairs"
64 );
65 assert!(
66 !used[sink],
67 "terminal vertices must be pairwise disjoint across pairs"
68 );
69 used[source] = true;
70 used[sink] = true;
71 }
72
73 Self {
74 graph,
75 terminal_pairs,
76 }
77 }
78
79 pub fn graph(&self) -> &G {
81 &self.graph
82 }
83
84 pub fn terminal_pairs(&self) -> &[(usize, usize)] {
86 &self.terminal_pairs
87 }
88
89 pub fn num_vertices(&self) -> usize {
91 self.graph.num_vertices()
92 }
93
94 pub fn num_edges(&self) -> usize {
96 self.graph.num_edges()
97 }
98
99 pub fn num_pairs(&self) -> usize {
101 self.terminal_pairs.len()
102 }
103
104 pub fn ordered_edges(&self) -> Vec<(usize, usize)> {
106 canonical_edges(&self.graph)
107 }
108
109 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
111 is_valid_disjoint_connecting_paths(&self.graph, &self.terminal_pairs, config)
112 }
113}
114
115impl<G> Problem for DisjointConnectingPaths<G>
116where
117 G: Graph + VariantParam,
118{
119 const NAME: &'static str = "DisjointConnectingPaths";
120 type Value = crate::types::Or;
121
122 fn variant() -> Vec<(&'static str, &'static str)> {
123 crate::variant_params![G]
124 }
125
126 fn dims(&self) -> Vec<usize> {
127 vec![2; self.num_edges()]
128 }
129
130 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
131 crate::types::Or(self.is_valid_solution(config))
132 }
133}
134
135fn canonical_edges<G: Graph>(graph: &G) -> Vec<(usize, usize)> {
136 let mut edges = graph
137 .edges()
138 .into_iter()
139 .map(|(u, v)| if u <= v { (u, v) } else { (v, u) })
140 .collect::<Vec<_>>();
141 edges.sort_unstable();
142 edges
143}
144
145fn normalize_edge(u: usize, v: usize) -> (usize, usize) {
146 if u <= v {
147 (u, v)
148 } else {
149 (v, u)
150 }
151}
152
153fn is_valid_disjoint_connecting_paths<G: Graph>(
154 graph: &G,
155 terminal_pairs: &[(usize, usize)],
156 config: &[usize],
157) -> bool {
158 let edges = canonical_edges(graph);
159 if config.len() != edges.len() {
160 return false;
161 }
162 if config.iter().any(|&value| value > 1) {
163 return false;
164 }
165
166 let num_vertices = graph.num_vertices();
167 let mut adjacency = vec![Vec::new(); num_vertices];
168 let mut degrees = vec![0usize; num_vertices];
169 for (index, &chosen) in config.iter().enumerate() {
170 if chosen == 1 {
171 let (u, v) = edges[index];
172 adjacency[u].push(v);
173 adjacency[v].push(u);
174 degrees[u] += 1;
175 degrees[v] += 1;
176 }
177 }
178
179 let mut terminal_vertices = vec![false; num_vertices];
180 let required_pairs = terminal_pairs
181 .iter()
182 .map(|&(u, v)| {
183 terminal_vertices[u] = true;
184 terminal_vertices[v] = true;
185 normalize_edge(u, v)
186 })
187 .collect::<BTreeSet<_>>();
188 let mut matched_pairs = BTreeSet::new();
189 let mut visited = vec![false; num_vertices];
190 let mut component_count = 0usize;
191
192 for start in 0..num_vertices {
193 if degrees[start] == 0 || visited[start] {
194 continue;
195 }
196
197 component_count += 1;
198 let mut stack = vec![start];
199 let mut vertices = Vec::new();
200 let mut degree_sum = 0usize;
201 visited[start] = true;
202
203 while let Some(vertex) = stack.pop() {
204 vertices.push(vertex);
205 degree_sum += degrees[vertex];
206 for &neighbor in &adjacency[vertex] {
207 if !visited[neighbor] {
208 visited[neighbor] = true;
209 stack.push(neighbor);
210 }
211 }
212 }
213
214 let edge_count = degree_sum / 2;
215 if edge_count + 1 != vertices.len() {
216 return false;
217 }
218
219 let mut endpoints = Vec::new();
220 for &vertex in &vertices {
221 match degrees[vertex] {
222 1 => endpoints.push(vertex),
223 2 => {
224 if terminal_vertices[vertex] {
225 return false;
226 }
227 }
228 _ => return false,
229 }
230 }
231
232 if endpoints.len() != 2 {
233 return false;
234 }
235
236 let realized_pair = normalize_edge(endpoints[0], endpoints[1]);
237 if !required_pairs.contains(&realized_pair) || !matched_pairs.insert(realized_pair) {
238 return false;
239 }
240 }
241
242 component_count == terminal_pairs.len() && matched_pairs.len() == terminal_pairs.len()
243}
244
245crate::declare_variants! {
246 default DisjointConnectingPaths<SimpleGraph> => "2^num_edges",
247}
248
249#[cfg(feature = "example-db")]
250pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
251 vec![crate::example_db::specs::ModelExampleSpec {
252 id: "disjoint_connecting_paths_simplegraph",
253 instance: Box::new(DisjointConnectingPaths::new(
254 SimpleGraph::new(
255 6,
256 vec![(0, 1), (1, 3), (0, 2), (1, 4), (2, 4), (3, 5), (4, 5)],
257 ),
258 vec![(0, 3), (2, 5)],
259 )),
260 optimal_config: vec![1, 0, 1, 0, 1, 0, 1],
261 optimal_value: serde_json::json!(true),
262 }]
263}
264
265#[cfg(test)]
266#[path = "../../unit_tests/models/graph/disjoint_connecting_paths.rs"]
267mod tests;