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