1use crate::registry::{FieldInfo, ProblemSchemaEntry, ProblemSizeFieldEntry};
17use crate::topology::{Graph, SimpleGraph};
18use crate::traits::Problem;
19use serde::{Deserialize, Serialize};
20use std::collections::VecDeque;
21
22inventory::submit! {
23 ProblemSchemaEntry {
24 name: "UndirectedFlowLowerBounds",
25 display_name: "Undirected Flow with Lower Bounds",
26 aliases: &[],
27 dimensions: &[],
28 module_path: module_path!(),
29 description: "Determine whether an undirected lower-bounded flow of value at least R exists",
30 fields: &[
31 FieldInfo { name: "graph", type_name: "SimpleGraph", description: "Undirected graph G=(V,E)" },
32 FieldInfo { name: "capacities", type_name: "Vec<u64>", description: "Upper capacities c(e) in graph edge order" },
33 FieldInfo { name: "lower_bounds", type_name: "Vec<u64>", description: "Lower bounds l(e) in graph edge order" },
34 FieldInfo { name: "source", type_name: "usize", description: "Source vertex s" },
35 FieldInfo { name: "sink", type_name: "usize", description: "Sink vertex t" },
36 FieldInfo { name: "requirement", type_name: "u64", description: "Required net inflow R at sink t" },
37 ],
38 }
39}
40
41inventory::submit! {
42 ProblemSizeFieldEntry {
43 name: "UndirectedFlowLowerBounds",
44 fields: &["num_vertices", "num_edges"],
45 }
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct UndirectedFlowLowerBounds {
50 graph: SimpleGraph,
51 capacities: Vec<u64>,
52 lower_bounds: Vec<u64>,
53 source: usize,
54 sink: usize,
55 requirement: u64,
56}
57
58impl UndirectedFlowLowerBounds {
59 pub fn new(
60 graph: SimpleGraph,
61 capacities: Vec<u64>,
62 lower_bounds: Vec<u64>,
63 source: usize,
64 sink: usize,
65 requirement: u64,
66 ) -> Self {
67 assert_eq!(
68 capacities.len(),
69 graph.num_edges(),
70 "capacities length must match graph num_edges"
71 );
72 assert_eq!(
73 lower_bounds.len(),
74 graph.num_edges(),
75 "lower_bounds length must match graph num_edges"
76 );
77
78 let num_vertices = graph.num_vertices();
79 assert!(
80 source < num_vertices,
81 "source must be less than num_vertices ({num_vertices})"
82 );
83 assert!(
84 sink < num_vertices,
85 "sink must be less than num_vertices ({num_vertices})"
86 );
87 assert!(source != sink, "source and sink must be distinct");
88 assert!(requirement >= 1, "requirement must be at least 1");
89
90 for (edge_index, (&lower, &upper)) in lower_bounds.iter().zip(&capacities).enumerate() {
91 assert!(
92 lower <= upper,
93 "lower bound at edge {edge_index} must be at most its capacity"
94 );
95 }
96
97 Self {
98 graph,
99 capacities,
100 lower_bounds,
101 source,
102 sink,
103 requirement,
104 }
105 }
106
107 pub fn graph(&self) -> &SimpleGraph {
108 &self.graph
109 }
110
111 pub fn capacities(&self) -> &[u64] {
112 &self.capacities
113 }
114
115 pub fn lower_bounds(&self) -> &[u64] {
116 &self.lower_bounds
117 }
118
119 pub fn source(&self) -> usize {
120 self.source
121 }
122
123 pub fn sink(&self) -> usize {
124 self.sink
125 }
126
127 pub fn requirement(&self) -> u64 {
128 self.requirement
129 }
130
131 pub fn num_vertices(&self) -> usize {
132 self.graph.num_vertices()
133 }
134
135 pub fn num_edges(&self) -> usize {
136 self.graph.num_edges()
137 }
138
139 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
140 self.evaluate(config).0
141 }
142
143 fn total_capacity(&self) -> Option<u128> {
144 self.capacities.iter().try_fold(0_u128, |acc, &capacity| {
145 acc.checked_add(u128::from(capacity))
146 })
147 }
148
149 fn has_feasible_orientation(&self, config: &[usize]) -> bool {
150 if config.len() != self.num_edges() {
151 return false;
152 }
153
154 let Some(total_capacity) = self.total_capacity() else {
155 return false;
156 };
157 let requirement = u128::from(self.requirement);
158 if requirement > total_capacity {
159 return false;
160 }
161
162 let node_count = self.num_vertices();
163 let super_source = node_count;
164 let super_sink = node_count + 1;
165 let mut network = ResidualNetwork::new(node_count + 2);
166 let mut balances = vec![0_i128; node_count];
167
168 for (edge_index, ((u, v), &orientation)) in self
169 .graph
170 .edges()
171 .into_iter()
172 .zip(config.iter())
173 .enumerate()
174 {
175 let (from, to) = match orientation {
176 0 => (u, v),
177 1 => (v, u),
178 _ => return false,
179 };
180 let lower = u128::from(self.lower_bounds[edge_index]);
181 let upper = u128::from(self.capacities[edge_index]);
182 if !add_lower_bounded_edge(&mut network, &mut balances, from, to, lower, upper) {
183 return false;
184 }
185 }
186
187 if !add_lower_bounded_edge(
188 &mut network,
189 &mut balances,
190 self.sink,
191 self.source,
192 requirement,
193 total_capacity,
194 ) {
195 return false;
196 }
197
198 let mut demand = 0_u128;
199 for (vertex, balance) in balances.into_iter().enumerate() {
200 if balance > 0 {
201 let needed = u128::try_from(balance).expect("positive i128 balance fits u128");
202 demand = match demand.checked_add(needed) {
203 Some(value) => value,
204 None => return false,
205 };
206 network.add_edge(super_source, vertex, needed);
207 } else if balance < 0 {
208 let needed = u128::try_from(-balance).expect("negative i128 balance fits u128");
209 network.add_edge(vertex, super_sink, needed);
210 }
211 }
212
213 network.max_flow(super_source, super_sink) == demand
214 }
215}
216
217impl Problem for UndirectedFlowLowerBounds {
218 const NAME: &'static str = "UndirectedFlowLowerBounds";
219 type Value = crate::types::Or;
220
221 fn variant() -> Vec<(&'static str, &'static str)> {
222 crate::variant_params![]
223 }
224
225 fn dims(&self) -> Vec<usize> {
226 vec![2; self.num_edges()]
227 }
228
229 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
230 crate::types::Or(self.has_feasible_orientation(config))
231 }
232}
233
234crate::declare_variants! {
235 default UndirectedFlowLowerBounds => "2^num_edges",
236}
237
238#[cfg(feature = "example-db")]
239pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
240 vec![crate::example_db::specs::ModelExampleSpec {
241 id: "undirected_flow_lower_bounds",
242 instance: Box::new(UndirectedFlowLowerBounds::new(
243 SimpleGraph::new(
244 6,
245 vec![(0, 1), (0, 2), (1, 3), (2, 3), (1, 4), (3, 5), (4, 5)],
246 ),
247 vec![2, 2, 2, 2, 1, 3, 2],
248 vec![1, 1, 0, 0, 1, 0, 1],
249 0,
250 5,
251 3,
252 )),
253 optimal_config: vec![0, 0, 0, 0, 0, 0, 0],
254 optimal_value: serde_json::json!(true),
255 }]
256}
257
258#[derive(Debug, Clone)]
259struct ResidualEdge {
260 to: usize,
261 rev: usize,
262 capacity: u128,
263}
264
265#[derive(Debug, Clone)]
266struct ResidualNetwork {
267 adjacency: Vec<Vec<ResidualEdge>>,
268}
269
270impl ResidualNetwork {
271 fn new(num_vertices: usize) -> Self {
272 Self {
273 adjacency: vec![Vec::new(); num_vertices],
274 }
275 }
276
277 fn add_edge(&mut self, from: usize, to: usize, capacity: u128) {
278 let reverse_at_to = self.adjacency[to].len();
279 let reverse_at_from = self.adjacency[from].len();
280 self.adjacency[from].push(ResidualEdge {
281 to,
282 rev: reverse_at_to,
283 capacity,
284 });
285 self.adjacency[to].push(ResidualEdge {
286 to: from,
287 rev: reverse_at_from,
288 capacity: 0,
289 });
290 }
291
292 fn max_flow(&mut self, source: usize, sink: usize) -> u128 {
293 let mut total_flow = 0_u128;
294
295 loop {
296 let mut parent: Vec<Option<(usize, usize)>> = vec![None; self.adjacency.len()];
297 let mut queue = VecDeque::new();
298 queue.push_back(source);
299 parent[source] = Some((source, usize::MAX));
300
301 while let Some(vertex) = queue.pop_front() {
302 if vertex == sink {
303 break;
304 }
305
306 for (edge_index, edge) in self.adjacency[vertex].iter().enumerate() {
307 if edge.capacity == 0 || parent[edge.to].is_some() {
308 continue;
309 }
310 parent[edge.to] = Some((vertex, edge_index));
311 queue.push_back(edge.to);
312 }
313 }
314
315 if parent[sink].is_none() {
316 return total_flow;
317 }
318
319 let mut path_flow = u128::MAX;
320 let mut vertex = sink;
321 while vertex != source {
322 let (prev, edge_index) = parent[vertex].expect("sink is reachable");
323 path_flow = path_flow.min(self.adjacency[prev][edge_index].capacity);
324 vertex = prev;
325 }
326
327 let mut vertex = sink;
328 while vertex != source {
329 let (prev, edge_index) = parent[vertex].expect("sink is reachable");
330 let reverse_edge = self.adjacency[prev][edge_index].rev;
331 self.adjacency[prev][edge_index].capacity -= path_flow;
332 self.adjacency[vertex][reverse_edge].capacity += path_flow;
333 vertex = prev;
334 }
335
336 total_flow += path_flow;
337 }
338 }
339}
340
341fn add_lower_bounded_edge(
342 network: &mut ResidualNetwork,
343 balances: &mut [i128],
344 from: usize,
345 to: usize,
346 lower: u128,
347 upper: u128,
348) -> bool {
349 if lower > upper {
350 return false;
351 }
352
353 let residual = upper - lower;
354 if residual > 0 {
355 network.add_edge(from, to, residual);
356 }
357
358 let Ok(lower_signed) = i128::try_from(lower) else {
359 return false;
360 };
361 balances[from] -= lower_signed;
362 balances[to] += lower_signed;
363 true
364}
365
366#[cfg(test)]
367#[path = "../../unit_tests/models/graph/undirected_flow_lower_bounds.rs"]
368mod tests;