1use crate::registry::{CreateSpec, ProblemSchemaEntry};
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 category: crate::registry::ProblemCategory::Graph,
29 module_path: module_path!(),
30 description: "Determine whether an undirected lower-bounded flow of value at least R exists",
31 fields: UndirectedFlowLowerBoundsCreateSpec::FIELDS,
32 }
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct UndirectedFlowLowerBounds {
37 graph: SimpleGraph,
38 capacities: Vec<i64>,
39 lower_bounds: Vec<i64>,
40 source: usize,
41 sink: usize,
42 requirement: i64,
43}
44
45#[derive(Debug, Deserialize, crate::CreateSpec)]
46struct UndirectedFlowLowerBoundsCreateSpec {
47 graph: SimpleGraph,
49 capacities: Vec<i64>,
51 lower_bounds: Vec<i64>,
53 source: usize,
55 sink: usize,
57 requirement: i64,
59}
60impl TryFrom<UndirectedFlowLowerBoundsCreateSpec> for UndirectedFlowLowerBounds {
61 type Error = crate::registry::ConstructionError;
62 fn try_from(spec: UndirectedFlowLowerBoundsCreateSpec) -> Result<Self, Self::Error> {
63 let edges = spec.graph.num_edges();
64 if spec.capacities.len() != edges {
65 return Err(format!(
66 "capacities has {} entries, expected {edges}",
67 spec.capacities.len()
68 )
69 .into());
70 }
71 if spec.lower_bounds.len() != edges {
72 return Err(format!(
73 "lower_bounds has {} entries, expected {edges}",
74 spec.lower_bounds.len()
75 )
76 .into());
77 }
78 let vertices = spec.graph.num_vertices();
79 if spec.source >= vertices || spec.sink >= vertices {
80 return Err("source and sink must be valid graph vertices"
81 .to_string()
82 .into());
83 }
84 if spec.source == spec.sink {
85 return Err("source and sink must be distinct".to_string().into());
86 }
87 if spec.requirement == 0 {
88 return Err("requirement must be at least 1".to_string().into());
89 }
90 if let Some((index, _)) = spec
91 .lower_bounds
92 .iter()
93 .zip(&spec.capacities)
94 .enumerate()
95 .find(|(_, (&lower, &upper))| lower > upper)
96 {
97 return Err(format!("lower bound at edge {index} exceeds its capacity").into());
98 }
99 Ok(Self::new(
100 spec.graph,
101 spec.capacities,
102 spec.lower_bounds,
103 spec.source,
104 spec.sink,
105 spec.requirement,
106 ))
107 }
108}
109
110impl UndirectedFlowLowerBounds {
111 pub fn new(
112 graph: SimpleGraph,
113 capacities: Vec<i64>,
114 lower_bounds: Vec<i64>,
115 source: usize,
116 sink: usize,
117 requirement: i64,
118 ) -> Self {
119 assert_eq!(
120 capacities.len(),
121 graph.num_edges(),
122 "capacities length must match graph num_edges"
123 );
124 assert_eq!(
125 lower_bounds.len(),
126 graph.num_edges(),
127 "lower_bounds length must match graph num_edges"
128 );
129
130 let num_vertices = graph.num_vertices();
131 assert!(
132 source < num_vertices,
133 "source must be less than num_vertices ({num_vertices})"
134 );
135 assert!(
136 sink < num_vertices,
137 "sink must be less than num_vertices ({num_vertices})"
138 );
139 assert!(source != sink, "source and sink must be distinct");
140 assert!(requirement >= 1, "requirement must be at least 1");
141
142 for (edge_index, (&lower, &upper)) in lower_bounds.iter().zip(&capacities).enumerate() {
143 assert!(
144 lower <= upper,
145 "lower bound at edge {edge_index} must be at most its capacity"
146 );
147 }
148
149 Self {
150 graph,
151 capacities,
152 lower_bounds,
153 source,
154 sink,
155 requirement,
156 }
157 }
158
159 pub fn graph(&self) -> &SimpleGraph {
160 &self.graph
161 }
162
163 pub fn capacities(&self) -> &[i64] {
164 &self.capacities
165 }
166
167 pub fn lower_bounds(&self) -> &[i64] {
168 &self.lower_bounds
169 }
170
171 pub fn source(&self) -> usize {
172 self.source
173 }
174
175 pub fn sink(&self) -> usize {
176 self.sink
177 }
178
179 pub fn requirement(&self) -> i64 {
180 self.requirement
181 }
182
183 pub fn num_vertices(&self) -> usize {
184 self.graph.num_vertices()
185 }
186
187 pub fn num_edges(&self) -> usize {
188 self.graph.num_edges()
189 }
190
191 pub fn is_valid_solution(
192 &self,
193 config: &[bool],
194 ) -> Result<bool, crate::traits::EvaluationError> {
195 if config.len() != self.num_edges() {
196 return Err(crate::traits::EvaluationError::InvalidConfiguration(
197 "edge-orientation length does not match the graph".into(),
198 ));
199 }
200 self.has_feasible_orientation(config)
201 }
202
203 fn total_capacity(&self) -> Result<i64, crate::traits::EvaluationError> {
204 self.capacities.iter().try_fold(0_i64, |total, &capacity| {
205 total.checked_add(capacity).ok_or_else(|| {
206 crate::traits::EvaluationError::IntegerOverflow(
207 "summing undirected flow capacities".into(),
208 )
209 })
210 })
211 }
212
213 fn has_feasible_orientation(
214 &self,
215 config: &[bool],
216 ) -> Result<bool, crate::traits::EvaluationError> {
217 if config.len() != self.num_edges() {
218 return Ok(false);
219 }
220
221 let total_capacity = self.total_capacity()?;
222 let requirement = self.requirement;
223 if requirement > total_capacity {
224 return Ok(false);
225 }
226
227 let node_count = self.num_vertices();
228 let super_source = node_count;
229 let super_sink = node_count + 1;
230 let mut network = ResidualNetwork::new(node_count + 2);
231 let mut balances = vec![0_i64; node_count];
232
233 for (edge_index, ((u, v), &orientation)) in self
234 .graph
235 .edges()
236 .into_iter()
237 .zip(config.iter())
238 .enumerate()
239 {
240 let (from, to) = if orientation { (v, u) } else { (u, v) };
241 let lower = self.lower_bounds[edge_index];
242 let upper = self.capacities[edge_index];
243 if !add_lower_bounded_edge(&mut network, &mut balances, from, to, lower, upper)? {
244 return Ok(false);
245 }
246 }
247
248 if !add_lower_bounded_edge(
249 &mut network,
250 &mut balances,
251 self.sink,
252 self.source,
253 requirement,
254 total_capacity,
255 )? {
256 return Ok(false);
257 }
258
259 let mut demand = 0_i64;
260 for (vertex, balance) in balances.into_iter().enumerate() {
261 if balance > 0 {
262 demand = match demand.checked_add(balance) {
263 Some(value) => value,
264 None => {
265 return Err(crate::traits::EvaluationError::IntegerOverflow(
266 "summing lower-bound flow demand".into(),
267 ));
268 }
269 };
270 network.add_edge(super_source, vertex, balance);
271 } else if balance < 0 {
272 network.add_edge(
273 vertex,
274 super_sink,
275 balance.checked_neg().ok_or_else(|| {
276 crate::traits::EvaluationError::IntegerOverflow(
277 "negating lower-bound flow balance".into(),
278 )
279 })?,
280 );
281 }
282 }
283
284 Ok(network.max_flow(super_source, super_sink)? == demand)
285 }
286}
287
288impl Problem for UndirectedFlowLowerBounds {
289 const NAME: &'static str = "UndirectedFlowLowerBounds";
290 type Solution = Vec<bool>;
291 type Value = crate::types::Or;
292
293 crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
294
295 fn variant() -> Vec<(&'static str, &'static str)> {
296 crate::variant_params![]
297 }
298
299 fn evaluate(
300 &self,
301 config: &Self::Solution,
302 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
303 Ok(crate::types::Or(self.is_valid_solution(config)?))
304 }
305}
306
307impl crate::solvers::BruteForceProblem for UndirectedFlowLowerBounds {
308 fn dimensions(&self) -> Vec<usize> {
309 vec![2; self.num_edges()]
310 }
311}
312
313crate::declare_variants! {
314 default UndirectedFlowLowerBounds => "2^num_edges" create UndirectedFlowLowerBoundsCreateSpec,
315}
316
317crate::register_brute_force! {
318 UndirectedFlowLowerBounds decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
319}
320
321#[cfg(feature = "example-db")]
322pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
323 vec![crate::example_db::specs::ModelExampleSpec {
324 id: "undirected_flow_lower_bounds",
325 instance: Box::new(UndirectedFlowLowerBounds::new(
326 SimpleGraph::new(
327 6,
328 vec![(0, 1), (0, 2), (1, 3), (2, 3), (1, 4), (3, 5), (4, 5)],
329 ),
330 vec![2, 2, 2, 2, 1, 3, 2],
331 vec![1, 1, 0, 0, 1, 0, 1],
332 0,
333 5,
334 3,
335 )),
336 optimal_config: serde_json::json!(vec![false, false, false, false, false, false, false]),
337 optimal_value: serde_json::json!(true),
338 }]
339}
340
341#[derive(Debug, Clone)]
342struct ResidualEdge {
343 to: usize,
344 rev: usize,
345 capacity: i64,
346}
347
348#[derive(Debug, Clone)]
349struct ResidualNetwork {
350 adjacency: Vec<Vec<ResidualEdge>>,
351}
352
353impl ResidualNetwork {
354 fn new(num_vertices: usize) -> Self {
355 Self {
356 adjacency: vec![Vec::new(); num_vertices],
357 }
358 }
359
360 fn add_edge(&mut self, from: usize, to: usize, capacity: i64) {
361 let reverse_at_to = self.adjacency[to].len();
362 let reverse_at_from = self.adjacency[from].len();
363 self.adjacency[from].push(ResidualEdge {
364 to,
365 rev: reverse_at_to,
366 capacity,
367 });
368 self.adjacency[to].push(ResidualEdge {
369 to: from,
370 rev: reverse_at_from,
371 capacity: 0,
372 });
373 }
374
375 fn max_flow(
376 &mut self,
377 source: usize,
378 sink: usize,
379 ) -> Result<i64, crate::traits::EvaluationError> {
380 let mut total_flow = 0_i64;
381
382 loop {
383 let mut parent: Vec<Option<(usize, usize)>> = vec![None; self.adjacency.len()];
384 let mut queue = VecDeque::new();
385 queue.push_back(source);
386 parent[source] = Some((source, usize::MAX));
387
388 while let Some(vertex) = queue.pop_front() {
389 if vertex == sink {
390 break;
391 }
392
393 for (edge_index, edge) in self.adjacency[vertex].iter().enumerate() {
394 if edge.capacity == 0 || parent[edge.to].is_some() {
395 continue;
396 }
397 parent[edge.to] = Some((vertex, edge_index));
398 queue.push_back(edge.to);
399 }
400 }
401
402 if parent[sink].is_none() {
403 return Ok(total_flow);
404 }
405
406 let mut path_flow = i64::MAX;
407 let mut vertex = sink;
408 while vertex != source {
409 let (prev, edge_index) = parent[vertex].expect("sink is reachable");
410 path_flow = path_flow.min(self.adjacency[prev][edge_index].capacity);
411 vertex = prev;
412 }
413
414 let mut vertex = sink;
415 while vertex != source {
416 let (prev, edge_index) = parent[vertex].expect("sink is reachable");
417 let reverse_edge = self.adjacency[prev][edge_index].rev;
418 self.adjacency[prev][edge_index].capacity = self.adjacency[prev][edge_index]
419 .capacity
420 .checked_sub(path_flow)
421 .ok_or_else(|| {
422 crate::traits::EvaluationError::IntegerOverflow(
423 "subtracting residual path flow".into(),
424 )
425 })?;
426 self.adjacency[vertex][reverse_edge].capacity = self.adjacency[vertex]
427 [reverse_edge]
428 .capacity
429 .checked_add(path_flow)
430 .ok_or_else(|| {
431 crate::traits::EvaluationError::IntegerOverflow(
432 "adding reverse residual path flow".into(),
433 )
434 })?;
435 vertex = prev;
436 }
437
438 total_flow = total_flow.checked_add(path_flow).ok_or_else(|| {
439 crate::traits::EvaluationError::IntegerOverflow("summing maximum flow".into())
440 })?;
441 }
442 }
443}
444
445fn add_lower_bounded_edge(
446 network: &mut ResidualNetwork,
447 balances: &mut [i64],
448 from: usize,
449 to: usize,
450 lower: i64,
451 upper: i64,
452) -> Result<bool, crate::traits::EvaluationError> {
453 if lower > upper {
454 return Ok(false);
455 }
456
457 let residual = upper.checked_sub(lower).ok_or_else(|| {
458 crate::traits::EvaluationError::IntegerOverflow(
459 "subtracting lower bound from flow capacity".into(),
460 )
461 })?;
462 if residual > 0 {
463 network.add_edge(from, to, residual);
464 }
465
466 balances[from] = balances[from].checked_sub(lower).ok_or_else(|| {
467 crate::traits::EvaluationError::IntegerOverflow(
468 "subtracting lower bound from source balance".into(),
469 )
470 })?;
471 balances[to] = balances[to].checked_add(lower).ok_or_else(|| {
472 crate::traits::EvaluationError::IntegerOverflow(
473 "adding lower bound to target balance".into(),
474 )
475 })?;
476 Ok(true)
477}
478
479#[cfg(test)]
480#[path = "../../unit_tests/models/graph/undirected_flow_lower_bounds.rs"]
481mod tests;