igraph/flow.rs
1//! Maximum flows, minimum cuts and graph connectivity (`igraph_flow.h`).
2//!
3//! A *flow network* is a graph whose edges carry a non-negative *capacity*
4//! (a `&[f64]` indexed by edge id; when omitted every edge has capacity 1).
5//! A *flow* from a `source` to a `target` assigns to every edge an amount not
6//! exceeding its capacity such that, at every other vertex, what comes in
7//! goes out. The celebrated *max-flow min-cut theorem* (Ford and Fulkerson,
8//! 1956) states that the largest possible flow value equals the smallest
9//! total capacity of a set of edges whose removal disconnects the target from
10//! the source. Almost everything in this module is built on that identity:
11//! edge and vertex connectivities, disjoint paths, cohesion measures and the
12//! Gomory–Hu tree are all max-flow computations in disguise.
13//!
14//! All the functions are methods of [`Graph`], with named result structs
15//! whenever the C function has several outputs.
16//!
17//! # Example: max-flow = min-cut
18//!
19//! ```
20//! use igraph::prelude::*;
21//!
22//! // A small directed pipeline network: 0 is the source, 3 the sink.
23//! let g = Graph::from_edges(&[(0, 1), (0, 2), (1, 2), (1, 3), (2, 3)], 4, true)?;
24//! let capacity = [3.0, 2.0, 1.0, 2.0, 3.0];
25//!
26//! let mf = g.maxflow(0, 3, Some(&capacity))?;
27//! assert_eq!(mf.value, 5.0);
28//!
29//! // The edges of the minimum cut have a total capacity equal to the flow.
30//! let cut_capacity: f64 = mf.cut.iter().map(|&e| capacity[e as usize]).sum();
31//! assert_eq!(cut_capacity, mf.value);
32//! assert_eq!(g.st_mincut_value(0, 3, Some(&capacity))?, 5.0);
33//!
34//! // The flow never exceeds the capacities.
35//! assert!(mf.flow.iter().zip(&capacity).all(|(f, c)| *f <= *c));
36//! # Ok::<(), igraph::Error>(())
37//! ```
38//!
39//! # Provided functionality
40//!
41//! | Method | C function | Computes |
42//! |---|---|---|
43//! | [`Graph::maxflow`] | `igraph_maxflow` | value, per-edge flow, minimum cut and both sides ([`MaxFlow`]) |
44//! | [`Graph::maxflow_value`] | `igraph_maxflow_value` | only the value of the maximum flow |
45//! | [`Graph::maxflow_value_with_stats`] | `igraph_maxflow_value` | value plus push-relabel statistics ([`MaxflowStats`]) |
46//! | [`Graph::st_mincut`] | `igraph_st_mincut` | minimum s-t cut ([`Cut`]) |
47//! | [`Graph::st_mincut_value`] | `igraph_st_mincut_value` | value of the minimum s-t cut |
48//! | [`Graph::mincut`] | `igraph_mincut` | minimum cut of the whole graph ([`Cut`]) |
49//! | [`Graph::mincut_value`] | `igraph_mincut_value` | value of the minimum cut of the whole graph |
50//! | [`Graph::st_vertex_connectivity`] | `igraph_st_vertex_connectivity` | vertex connectivity of a pair |
51//! | [`Graph::vertex_connectivity`] | `igraph_vertex_connectivity` | vertex connectivity of the graph |
52//! | [`Graph::st_edge_connectivity`] | `igraph_st_edge_connectivity` | edge connectivity of a pair |
53//! | [`Graph::edge_connectivity`] | `igraph_edge_connectivity` | edge connectivity of the graph |
54//! | [`Graph::edge_disjoint_paths`] | `igraph_edge_disjoint_paths` | number of edge-disjoint paths |
55//! | [`Graph::vertex_disjoint_paths`] | `igraph_vertex_disjoint_paths` | number of vertex-disjoint paths |
56//! | [`Graph::adhesion`] | `igraph_adhesion` | White–Harary adhesion (edge connectivity) |
57//! | [`Graph::cohesion`] | `igraph_cohesion` | White–Harary cohesion (vertex connectivity) |
58//! | [`Graph::even_tarjan_reduction`] | `igraph_even_tarjan_reduction` | vertex-splitting reduction ([`EvenTarjanReduction`]) |
59//! | [`Graph::residual_graph`] | `igraph_residual_graph` | residual network of a flow ([`ResidualGraph`]) |
60//! | [`Graph::reverse_residual_graph`] | `igraph_reverse_residual_graph` | reverse residual network of a flow |
61//! | [`Graph::dominator_tree`] | `igraph_dominator_tree` | Lengauer–Tarjan dominator tree ([`DominatorTree`]) |
62//! | [`Graph::all_st_cuts`] | `igraph_all_st_cuts` | every s-t edge cut ([`StCuts`]) |
63//! | [`Graph::all_st_mincuts`] | `igraph_all_st_mincuts` | every minimum s-t edge cut ([`StMinCuts`]) |
64//! | [`Graph::gomory_hu_tree`] | `igraph_gomory_hu_tree` | Gomory–Hu tree of all pairwise flows ([`GomoryHuTree`]) |
65//!
66//! Capacities must be finite and non-negative and, when given, have exactly
67//! one entry per edge. igraph itself does not validate the sign of the
68//! capacities (negative or NaN entries silently produce meaningless flows, and
69//! infinite ones produce NaN flows), so a wrong length or an invalid entry is
70//! reported as [`ErrorKind::InvalidValue`] before calling into C.
71//!
72//! The C reference for everything here is the
73//! [Flows chapter](https://igraph.org/c/html/latest/igraph-Flows.html) of the
74//! igraph manual.
75//!
76//! # Example: how robust is the karate club?
77//!
78//! ```
79//! use igraph::prelude::*;
80//!
81//! let karate = Graph::famous("Zachary")?;
82//! // Vertex 11 has a single friend, so one edge (or one vertex) isolates it.
83//! assert_eq!(karate.edge_connectivity(true)?, 1);
84//! assert_eq!(karate.vertex_connectivity(true)?, 1);
85//! // The two leaders, 0 and 33, are far better connected: by Menger's
86//! // theorem their local edge connectivity is the number of edge-disjoint
87//! // paths, which is also the unit-capacity maximum flow.
88//! let k = karate.st_edge_connectivity(0, 33)?;
89//! assert_eq!(k, 10);
90//! assert_eq!(karate.edge_disjoint_paths(0, 33)?, k);
91//! assert_eq!(karate.maxflow_value(0, 33, None)?, k as f64);
92//! // A Gomory–Hu tree stores all 561 pairwise flows in 33 numbers.
93//! let gh = karate.gomory_hu_tree(None)?;
94//! assert_eq!(gh.flow_between(0, 33), Some(k as f64));
95//! # Ok::<(), igraph::Error>(())
96//! ```
97//!
98//! # See also
99//!
100//! - [`Graph::is_connected`], [`Graph::articulation_points`] and
101//! [`Graph::bridges`] answer the "connectivity at least 1 / 2" questions in
102//! linear time, without any flow computation.
103//! - [`Graph::minimum_size_separators`], [`Graph::all_minimal_st_separators`],
104//! [`Graph::is_separator`] and [`Graph::cohesive_blocks`] list the vertex
105//! sets behind [`Graph::vertex_connectivity`].
106//! - [`Graph::maximum_bipartite_matching`] solves the classic flow
107//! application of matching the two sides of a bipartite graph.
108//! - [`Graph::read_graph_dimacs_flow`] and [`Graph::write_graph_dimacs_flow`]
109//! read and write maximum flow instances in the DIMACS format.
110
111use crate::{
112 constants::{NeighborMode, VconnNei},
113 error::{Error, ErrorKind, Result},
114 ffi::*,
115 graph::{EdgeId, Graph, VertexId},
116 igraph_call,
117 list::VectorIntList,
118 vector::{Vector, VectorInt},
119};
120use std::collections::VecDeque;
121
122// ---------------------------------------------------------------------------
123// Result types
124// ---------------------------------------------------------------------------
125
126/// Statistics collected by igraph's push-relabel maximum flow solver
127/// (`igraph_maxflow_stats_t`).
128///
129/// They are mostly interesting for benchmarking and for understanding how
130/// much work the Goldberg–Tarjan algorithm had to do on a given network.
131/// For undirected graphs igraph solves the flow problem on a directed graph
132/// with every edge doubled, and the statistics refer to that graph.
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
134pub struct MaxflowStats {
135 /// Number of push operations performed (`nopush`).
136 pub pushes: i64,
137 /// Number of relabel operations performed (`norelabel`).
138 pub relabels: i64,
139 /// Number of times the gap heuristic was applied (`nogap`).
140 pub gaps: i64,
141 /// Total number of vertices removed from further consideration by the
142 /// gap heuristic (`nogapnodes`).
143 pub gap_nodes: i64,
144 /// Number of reverse breadth-first searches used to (re)compute the
145 /// height function (`nobfs`); always at least one, as one runs before
146 /// the algorithm starts.
147 pub bfs_runs: i64,
148}
149
150impl From<igraph_maxflow_stats_t> for MaxflowStats {
151 fn from(s: igraph_maxflow_stats_t) -> Self {
152 Self {
153 pushes: s.nopush,
154 relabels: s.norelabel,
155 gaps: s.nogap,
156 gap_nodes: s.nogapnodes,
157 bfs_runs: s.nobfs,
158 }
159 }
160}
161
162fn empty_stats() -> igraph_maxflow_stats_t {
163 igraph_maxflow_stats_t {
164 nopush: 0,
165 norelabel: 0,
166 nogap: 0,
167 nogapnodes: 0,
168 nobfs: 0,
169 }
170}
171
172/// A maximum flow between two vertices, as computed by [`Graph::maxflow`].
173#[derive(Debug, Clone, PartialEq)]
174pub struct MaxFlow {
175 /// The value of the maximum flow, i.e. the net amount entering the target.
176 pub value: f64,
177 /// The flow on each edge, indexed by edge id.
178 ///
179 /// In **undirected** graphs the sign encodes the direction: a positive
180 /// value means the flow goes from the smaller vertex id to the larger
181 /// one, a negative value the other way round.
182 pub flow: Vec<f64>,
183 /// Ids of the edges of the minimum cut corresponding to this flow; their
184 /// capacities sum up to [`value`](Self::value).
185 pub cut: Vec<EdgeId>,
186 /// The side of the minimum cut containing the source.
187 pub partition: Vec<VertexId>,
188 /// The side of the minimum cut containing the target.
189 pub partition2: Vec<VertexId>,
190 /// Operation counts of the push-relabel solver.
191 pub stats: MaxflowStats,
192}
193
194/// An edge cut splitting the vertices into two sides, as returned by
195/// [`Graph::st_mincut`] and [`Graph::mincut`].
196#[derive(Debug, Clone, PartialEq)]
197pub struct Cut {
198 /// Total capacity of the edges in the cut.
199 pub value: f64,
200 /// Ids of the edges in the cut.
201 pub cut: Vec<EdgeId>,
202 /// Vertices of the first side (for s-t cuts, the side of the source).
203 pub partition: Vec<VertexId>,
204 /// Vertices of the second side (for s-t cuts, the side of the target).
205 pub partition2: Vec<VertexId>,
206}
207
208/// The Even–Tarjan reduction of a graph, see [`Graph::even_tarjan_reduction`].
209#[derive(Debug, Clone, PartialEq)]
210pub struct EvenTarjanReduction {
211 /// The reduced directed graph, with `2 n` vertices and `n + 2 m` edges.
212 pub graph: Graph,
213 /// Capacities of the reduced graph's edges: 1 for the first `n` edges
214 /// (the `i' → i''` "vertex" edges) and `n` (standing for infinity) for the
215 /// remaining `2 m` edges.
216 pub capacity: Vec<f64>,
217}
218
219/// The residual network of a flow, see [`Graph::residual_graph`].
220#[derive(Debug, Clone, PartialEq)]
221pub struct ResidualGraph {
222 /// The directed residual graph, on the same vertex set as the original.
223 pub graph: Graph,
224 /// Residual capacity (capacity minus flow) of each edge of
225 /// [`graph`](Self::graph), in edge-id order.
226 pub capacity: Vec<f64>,
227}
228
229/// A dominator tree of a flowgraph, see [`Graph::dominator_tree`].
230#[derive(Debug, Clone, PartialEq)]
231pub struct DominatorTree {
232 /// Immediate dominator of each vertex, indexed by vertex id. It is
233 /// `None` for the root itself and for the vertices unreachable from the
234 /// root (the latter are also listed in [`leftout`](Self::leftout)).
235 pub dom: Vec<Option<VertexId>>,
236 /// The dominator tree as a directed graph on the same vertex set, with an
237 /// edge from `idom(w)` to `w` (reversed when `mode` is
238 /// [`NeighborMode::In`]); unreachable vertices are isolated.
239 pub tree: Graph,
240 /// Ids of the vertices that are not reachable from the root.
241 pub leftout: Vec<VertexId>,
242}
243
244impl DominatorTree {
245 /// Returns the chain of dominators of `v`, from its immediate dominator up
246 /// to the root (empty for the root and for unreachable vertices).
247 ///
248 /// Panics if `v` is not a valid vertex id of the analysed graph.
249 pub fn dominators(&self, v: VertexId) -> Vec<VertexId> {
250 let mut chain = vec![];
251 let mut cur = self.dom[v as usize];
252 while let Some(d) = cur {
253 chain.push(d);
254 cur = self.dom[d as usize];
255 }
256 chain
257 }
258}
259
260/// All minimal s-t edge cuts of a directed graph, see [`Graph::all_st_cuts`].
261#[derive(Debug, Clone, PartialEq)]
262pub struct StCuts {
263 /// Every minimal cut, as a list of edge ids.
264 pub cuts: Vec<Vec<EdgeId>>,
265 /// For each cut, the vertex set `X` generating it: the cut consists of
266 /// all edges from `X` to its complement. `X` contains the source.
267 pub partition1s: Vec<Vec<VertexId>>,
268}
269
270/// All minimum s-t edge cuts of a directed graph, see
271/// [`Graph::all_st_mincuts`].
272#[derive(Debug, Clone, PartialEq)]
273pub struct StMinCuts {
274 /// The (common) total capacity of the minimum cuts.
275 pub value: f64,
276 /// Every minimum cut, as a list of edge ids.
277 pub cuts: Vec<Vec<EdgeId>>,
278 /// For each cut, the vertex set `X` generating it: the cut consists of
279 /// all edges from `X` to its complement. `X` contains the source.
280 pub partition1s: Vec<Vec<VertexId>>,
281}
282
283/// A Gomory–Hu tree, see [`Graph::gomory_hu_tree`].
284#[derive(Debug, Clone, PartialEq)]
285pub struct GomoryHuTree {
286 /// The tree: an undirected graph on the same vertices as the input graph,
287 /// with `n - 1` edges (edge `i - 1` joins vertex `i` to its tree parent).
288 pub tree: Graph,
289 /// The flow value annotating each tree edge, indexed by tree edge id.
290 pub flows: Vec<f64>,
291}
292
293impl GomoryHuTree {
294 /// The maximum flow (equivalently, minimum cut) value between `u` and `v`
295 /// in the *original* graph, read off the tree as the minimum edge
296 /// annotation along the tree path from `u` to `v`.
297 ///
298 /// Returns `None` when `u == v` or when either id is out of range.
299 pub fn flow_between(&self, u: VertexId, v: VertexId) -> Option<f64> {
300 let n = self.tree.vcount();
301 if u == v || u < 0 || v < 0 || u as usize >= n || v as usize >= n {
302 return None;
303 }
304 let mut adj: Vec<Vec<(usize, f64)>> = vec![vec![]; n];
305 for (e, (a, b)) in self.tree.edge_list().into_iter().enumerate() {
306 adj[a as usize].push((b as usize, self.flows[e]));
307 adj[b as usize].push((a as usize, self.flows[e]));
308 }
309 // Breadth-first search carrying the bottleneck along the path.
310 let mut best = vec![None; n];
311 best[u as usize] = Some(f64::INFINITY);
312 let mut queue = VecDeque::from([u as usize]);
313 while let Some(x) = queue.pop_front() {
314 let bx = best[x]?;
315 for &(y, f) in &adj[x] {
316 if best[y].is_none() {
317 best[y] = Some(bx.min(f));
318 queue.push_back(y);
319 }
320 }
321 }
322 best[v as usize]
323 }
324}
325
326// ---------------------------------------------------------------------------
327// Private helpers
328// ---------------------------------------------------------------------------
329
330fn check_len(what: &str, data: &[f64], expected: usize) -> Result<()> {
331 if data.len() != expected {
332 return Err(Error::invalid(format!(
333 "the {what} vector has length {}, but the graph has {expected} edges",
334 data.len()
335 )));
336 }
337 Ok(())
338}
339
340/// Validates an optional capacity vector: one finite, non-negative entry per
341/// edge.
342///
343/// The length check is required for soundness, not just for nicer errors:
344/// for undirected graphs `igraph_maxflow` (and therefore every function built
345/// on it, including `igraph_gomory_hu_tree`) reads `capacity[i]` for every
346/// edge *before* validating the length (`igraph_i_maxflow_undirected` in
347/// `flow.c`, igraph 1.0.0 and 1.0.1), so a short vector would be read out of
348/// bounds.
349fn check_capacity(graph: &Graph, capacity: Option<&[f64]>) -> Result<()> {
350 let Some(c) = capacity else {
351 return Ok(());
352 };
353 check_len("capacity", c, graph.ecount())?;
354 match c.iter().position(|x| !(x.is_finite() && *x >= 0.0)) {
355 Some(e) => Err(Error::invalid(format!(
356 "capacities must be finite and non-negative, but edge {e} has capacity {}",
357 c[e]
358 ))),
359 None => Ok(()),
360 }
361}
362
363fn check_vertex(graph: &Graph, v: VertexId, what: &str) -> Result<()> {
364 if v < 0 || v as usize >= graph.vcount() {
365 return Err(Error::new(
366 ErrorKind::InvalidVertexId,
367 format!("invalid {what} vertex id {v}"),
368 ));
369 }
370 Ok(())
371}
372
373fn check_pair(graph: &Graph, source: VertexId, target: VertexId) -> Result<()> {
374 check_vertex(graph, source, "source")?;
375 check_vertex(graph, target, "target")
376}
377
378fn count(value: igraph_int_t) -> usize {
379 usize::try_from(value).unwrap_or(0)
380}
381
382// ---------------------------------------------------------------------------
383// Methods
384// ---------------------------------------------------------------------------
385
386impl igraph_t {
387 /// Maximum flow between `source` and `target`, together with a minimum
388 /// cut certifying its optimality.
389 ///
390 /// Uses the push-relabel algorithm of Goldberg and Tarjan (J. ACM 35(4),
391 /// 1988). A flow assigns to each edge a non-negative amount not larger
392 /// than its capacity, and conserves the flow at every vertex other than
393 /// the source and the target; its value is the net flow entering the
394 /// target. The result contains the value, the flow on every edge, the
395 /// edges of the corresponding minimum cut and the two sides of that cut
396 /// (the first containing the source, the second the target).
397 ///
398 /// Works on directed and undirected graphs; in undirected graphs the sign
399 /// of [`MaxFlow::flow`] tells the direction (see its docs).
400 /// `capacity` gives one non-negative capacity per edge; `None` means that
401 /// every edge has capacity 1.
402 ///
403 /// Time complexity: O(|V|³), usually much faster in practice.
404 ///
405 /// See also [`Graph::residual_graph`] to inspect the leftover capacities,
406 /// and [`Graph::maximum_bipartite_matching`] for the matching problem that
407 /// is usually solved as a unit-capacity flow.
408 ///
409 /// Binds [`igraph_maxflow`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_maxflow).
410 ///
411 /// # Errors
412 ///
413 /// [`ErrorKind::InvalidVertexId`] for invalid `source`/`target`,
414 /// [`ErrorKind::InvalidValue`] if they coincide, or if the capacity vector
415 /// has the wrong length or a negative, NaN or infinite entry.
416 ///
417 /// # Examples
418 ///
419 /// The example of igraph's `flow2.c`:
420 ///
421 /// ```
422 /// use igraph::prelude::*;
423 ///
424 /// let g = Graph::from_edges(
425 /// &[(0, 1), (1, 2), (2, 3), (0, 5), (5, 4), (4, 3), (3, 0)], 6, true)?;
426 /// let capacity = [3.0, 1.0, 2.0, 10.0, 1.0, 3.0, 2.0];
427 /// let mf = g.maxflow(0, 2, Some(&capacity))?;
428 /// assert_eq!(mf.value, 1.0);
429 /// assert_eq!(mf.flow, vec![1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0]);
430 /// assert_eq!(mf.partition, vec![0, 1, 3, 4, 5]);
431 /// assert_eq!(mf.partition2, vec![2]);
432 /// assert_eq!(mf.cut, vec![1]); // the edge 1 -> 2
433 /// assert!(mf.stats.bfs_runs >= 1);
434 /// # Ok::<(), igraph::Error>(())
435 /// ```
436 pub fn maxflow(
437 &self,
438 source: VertexId,
439 target: VertexId,
440 capacity: Option<&[f64]>,
441 ) -> Result<MaxFlow> {
442 check_capacity(self, capacity)?;
443 check_pair(self, source, target)?;
444 let cap = capacity.map(Vector::view);
445 let cap_ptr = cap.as_ref().map_or(std::ptr::null(), |v| v.as_ptr());
446 let mut value = 0.0;
447 let mut flow = Vector::new();
448 let mut cut = VectorInt::new();
449 let mut partition = VectorInt::new();
450 let mut partition2 = VectorInt::new();
451 let mut stats = empty_stats();
452 igraph_call!(igraph_maxflow(
453 self,
454 &mut value,
455 &mut flow,
456 &mut cut,
457 &mut partition,
458 &mut partition2,
459 source,
460 target,
461 cap_ptr,
462 &mut stats,
463 ))?;
464 Ok(MaxFlow {
465 value,
466 flow: flow.into(),
467 cut: cut.into(),
468 partition: partition.into(),
469 partition2: partition2.into(),
470 stats: stats.into(),
471 })
472 }
473
474 /// Value of the maximum flow between `source` and `target`.
475 ///
476 /// Same algorithm as [`Graph::maxflow`], but only the value is computed.
477 /// By the max-flow min-cut theorem this equals
478 /// [`Graph::st_mincut_value`]. `capacity: None` means unit capacities.
479 ///
480 /// Time complexity: O(|V|³).
481 ///
482 /// Binds [`igraph_maxflow_value`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_maxflow_value).
483 ///
484 /// # Errors
485 ///
486 /// As for [`Graph::maxflow`].
487 ///
488 /// # Examples
489 ///
490 /// ```
491 /// use igraph::prelude::*;
492 ///
493 /// // Two parallel routes from 0 to 3, each able to carry one unit.
494 /// let g = Graph::from_edges(&[(0, 1), (1, 3), (0, 2), (2, 3)], 4, true)?;
495 /// assert_eq!(g.maxflow_value(0, 3, None)?, 2.0);
496 /// assert_eq!(g.maxflow_value(0, 3, Some(&[5.0, 1.0, 2.0, 7.0]))?, 3.0);
497 /// # Ok::<(), igraph::Error>(())
498 /// ```
499 pub fn maxflow_value(
500 &self,
501 source: VertexId,
502 target: VertexId,
503 capacity: Option<&[f64]>,
504 ) -> Result<f64> {
505 self.maxflow_value_with_stats(source, target, capacity)
506 .map(|(value, _)| value)
507 }
508
509 /// Value of the maximum flow between `source` and `target`, together with
510 /// the operation counts of the push-relabel solver.
511 ///
512 /// See [`Graph::maxflow_value`] and [`MaxflowStats`].
513 ///
514 /// Binds [`igraph_maxflow_value`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_maxflow_value).
515 ///
516 /// # Errors
517 ///
518 /// As for [`Graph::maxflow`].
519 ///
520 /// # Examples
521 ///
522 /// ```
523 /// use igraph::prelude::*;
524 ///
525 /// let g = Graph::famous("Zachary")?;
526 /// let (value, stats) = g.maxflow_value_with_stats(0, 33, None)?;
527 /// assert_eq!(value, 10.0);
528 /// assert!(stats.pushes > 0);
529 /// assert!(stats.bfs_runs >= 1); // the initial global relabelling
530 /// # Ok::<(), igraph::Error>(())
531 /// ```
532 pub fn maxflow_value_with_stats(
533 &self,
534 source: VertexId,
535 target: VertexId,
536 capacity: Option<&[f64]>,
537 ) -> Result<(f64, MaxflowStats)> {
538 check_capacity(self, capacity)?;
539 check_pair(self, source, target)?;
540 let cap = capacity.map(Vector::view);
541 let cap_ptr = cap.as_ref().map_or(std::ptr::null(), |v| v.as_ptr());
542 let mut value = 0.0;
543 let mut stats = empty_stats();
544 igraph_call!(igraph_maxflow_value(
545 self, &mut value, source, target, cap_ptr, &mut stats
546 ))?;
547 Ok((value, stats.into()))
548 }
549
550 /// Minimum cut between a source and a target vertex.
551 ///
552 /// Finds the edge set of smallest total capacity whose removal eliminates
553 /// all (directed, in directed graphs) paths from `source` to `target`,
554 /// together with the two sides of the cut (the first contains the source,
555 /// the second the target). Computed with [`Graph::maxflow`].
556 /// `capacity: None` means unit capacities.
557 ///
558 /// Binds [`igraph_st_mincut`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_st_mincut).
559 ///
560 /// # Errors
561 ///
562 /// As for [`Graph::maxflow`].
563 ///
564 /// # Examples
565 ///
566 /// ```
567 /// use igraph::prelude::*;
568 ///
569 /// // A "barbell": two triangles joined by the bridge 2 - 3.
570 /// let g = Graph::from_edges(
571 /// &[(0, 1), (1, 2), (2, 0), (2, 3), (3, 4), (4, 5), (5, 3)], 6, false)?;
572 /// let cut = g.st_mincut(0, 5, None)?;
573 /// assert_eq!(cut.value, 1.0);
574 /// assert_eq!(cut.cut, vec![3]);
575 /// assert_eq!(cut.partition, vec![0, 1, 2]);
576 /// assert_eq!(cut.partition2, vec![3, 4, 5]);
577 /// # Ok::<(), igraph::Error>(())
578 /// ```
579 pub fn st_mincut(
580 &self,
581 source: VertexId,
582 target: VertexId,
583 capacity: Option<&[f64]>,
584 ) -> Result<Cut> {
585 check_capacity(self, capacity)?;
586 check_pair(self, source, target)?;
587 let cap = capacity.map(Vector::view);
588 let cap_ptr = cap.as_ref().map_or(std::ptr::null(), |v| v.as_ptr());
589 let mut value = 0.0;
590 let mut cut = VectorInt::new();
591 let mut partition = VectorInt::new();
592 let mut partition2 = VectorInt::new();
593 igraph_call!(igraph_st_mincut(
594 self,
595 &mut value,
596 &mut cut,
597 &mut partition,
598 &mut partition2,
599 source,
600 target,
601 cap_ptr,
602 ))?;
603 Ok(Cut {
604 value,
605 cut: cut.into(),
606 partition: partition.into(),
607 partition2: partition2.into(),
608 })
609 }
610
611 /// Value of the minimum cut between a source and a target vertex.
612 ///
613 /// The minimum total capacity of edges to remove in order to eliminate
614 /// all paths from `source` to `target` (directed paths in directed
615 /// graphs). Equal to [`Graph::maxflow_value`] by the max-flow min-cut
616 /// theorem. `capacity: None` means unit capacities.
617 ///
618 /// Time complexity: O(|V|³).
619 ///
620 /// Binds [`igraph_st_mincut_value`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_st_mincut_value).
621 ///
622 /// # Errors
623 ///
624 /// As for [`Graph::maxflow`].
625 ///
626 /// # Examples
627 ///
628 /// The network of igraph's `igraph_st_mincut_value.c` unit test:
629 ///
630 /// ```
631 /// use igraph::prelude::*;
632 ///
633 /// let g = Graph::from_edges(
634 /// &[(0, 1), (0, 2), (1, 2), (1, 3), (2, 4), (3, 4), (3, 5), (4, 5)], 6, true)?;
635 /// let capacity = [5.0, 2.0, 2.0, 3.0, 4.0, 1.0, 2.0, 5.0];
636 /// assert_eq!(g.st_mincut_value(0, 5, Some(&capacity))?, 7.0);
637 /// assert_eq!(g.maxflow_value(0, 5, Some(&capacity))?, 7.0);
638 /// # Ok::<(), igraph::Error>(())
639 /// ```
640 pub fn st_mincut_value(
641 &self,
642 source: VertexId,
643 target: VertexId,
644 capacity: Option<&[f64]>,
645 ) -> Result<f64> {
646 check_capacity(self, capacity)?;
647 check_pair(self, source, target)?;
648 let cap = capacity.map(Vector::view);
649 let cap_ptr = cap.as_ref().map_or(std::ptr::null(), |v| v.as_ptr());
650 let mut value = 0.0;
651 igraph_call!(igraph_st_mincut_value(
652 self, &mut value, source, target, cap_ptr
653 ))?;
654 Ok(value)
655 }
656
657 /// Minimum cut of the whole graph.
658 ///
659 /// The set of edges of minimum total capacity whose removal disconnects
660 /// the graph (makes it not strongly connected, for directed graphs),
661 /// with the two resulting vertex sides. Undirected graphs use the
662 /// Stoer–Wagner algorithm (J. ACM 44, 1997), in
663 /// O(|V||E| + |V|² log |V|); directed graphs compute 2|V| − 2 maximum
664 /// flows, O(|V|⁴). `capacity: None` means unit capacities.
665 ///
666 /// If the graph is already disconnected the value is 0 and the cut empty.
667 /// Degenerate graphs follow igraph: with a single vertex (or a directed
668 /// graph with no vertices) the value is `f64::INFINITY` and the cut empty,
669 /// while an undirected graph with no vertices counts as disconnected
670 /// (value 0, everything empty).
671 ///
672 /// Note: for directed graphs `igraph_mincut` discards the error code of
673 /// its internal max-flow computations (still the case in igraph 1.0.0 and
674 /// 1.0.1). The only such errors not already excluded by the argument
675 /// checks of this wrapper are out-of-memory conditions.
676 ///
677 /// See also [`Graph::edge_connectivity`] (the unit-capacity value) and
678 /// [`Graph::bridges`] (all the edges forming a cut of size one).
679 ///
680 /// Binds [`igraph_mincut`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_mincut).
681 ///
682 /// # Errors
683 ///
684 /// [`ErrorKind::InvalidValue`] if the capacity vector has the wrong length
685 /// or an entry that is negative, NaN or infinite.
686 ///
687 /// # Examples
688 ///
689 /// The weighted example of igraph's `igraph_mincut.c`:
690 ///
691 /// ```
692 /// use igraph::prelude::*;
693 ///
694 /// let g = Graph::from_edges(&[
695 /// (0, 1), (0, 4), (1, 2), (1, 4), (1, 5), (2, 3),
696 /// (2, 6), (3, 6), (3, 7), (4, 5), (5, 6), (6, 7),
697 /// ], 8, false)?;
698 /// let w = [2.0, 3.0, 3.0, 2.0, 2.0, 4.0, 2.0, 2.0, 2.0, 3.0, 1.0, 3.0];
699 /// let cut = g.mincut(Some(&w))?;
700 /// assert_eq!(cut.value, 4.0);
701 /// assert_eq!(cut.partition, vec![2, 3, 6, 7]);
702 /// assert_eq!(cut.partition2, vec![0, 1, 4, 5]);
703 /// assert_eq!(cut.cut, vec![2, 10]); // 1-2 and 5-6
704 /// # Ok::<(), igraph::Error>(())
705 /// ```
706 pub fn mincut(&self, capacity: Option<&[f64]>) -> Result<Cut> {
707 check_capacity(self, capacity)?;
708 if self.vcount() == 0 && !self.is_directed() {
709 // igraph (1.0.0 and 1.0.1) handles this case as "disconnected"
710 // and sizes the first side from `csize[0]` of an *empty*
711 // component-size vector, i.e. past its logical end. Answer
712 // directly with the same result instead of relying on that.
713 return Ok(Cut {
714 value: 0.0,
715 cut: vec![],
716 partition: vec![],
717 partition2: vec![],
718 });
719 }
720 let cap = capacity.map(Vector::view);
721 let cap_ptr = cap.as_ref().map_or(std::ptr::null(), |v| v.as_ptr());
722 let mut value = 0.0;
723 let mut cut = VectorInt::new();
724 let mut partition = VectorInt::new();
725 let mut partition2 = VectorInt::new();
726 igraph_call!(igraph_mincut(
727 self,
728 &mut value,
729 &mut partition,
730 &mut partition2,
731 &mut cut,
732 cap_ptr,
733 ))?;
734 Ok(Cut {
735 value,
736 cut: cut.into(),
737 partition: partition.into(),
738 partition2: partition2.into(),
739 })
740 }
741
742 /// Value of the minimum cut of the whole graph.
743 ///
744 /// The minimum total capacity of edges whose removal makes the graph not
745 /// strongly connected (0 if it is already disconnected). Uses
746 /// Stoer–Wagner for undirected graphs, O(log|V| · |V|²), and maximum flows
747 /// from a fixed vertex in both directions for directed graphs, O(|V|⁴).
748 /// For a single vertex, or a directed graph without vertices, the result
749 /// is `f64::INFINITY`; an undirected graph without vertices counts as
750 /// disconnected and gives 0. `capacity: None` means unit capacities.
751 ///
752 /// Binds [`igraph_mincut_value`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_mincut_value).
753 ///
754 /// # Errors
755 ///
756 /// [`ErrorKind::InvalidValue`] if the capacity vector has the wrong length
757 /// or an entry that is negative, NaN or infinite.
758 ///
759 /// # Examples
760 ///
761 /// ```
762 /// use igraph::prelude::*;
763 ///
764 /// // A directed cycle is strongly connected, but a single edge breaks it.
765 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, true)?;
766 /// assert_eq!(g.mincut_value(None)?, 1.0);
767 /// assert_eq!(g.mincut_value(Some(&[4.0, 2.5, 3.0]))?, 2.5);
768 /// # Ok::<(), igraph::Error>(())
769 /// ```
770 pub fn mincut_value(&self, capacity: Option<&[f64]>) -> Result<f64> {
771 check_capacity(self, capacity)?;
772 let cap = capacity.map(Vector::view);
773 let cap_ptr = cap.as_ref().map_or(std::ptr::null(), |v| v.as_ptr());
774 let mut value = 0.0;
775 igraph_call!(igraph_mincut_value(self, &mut value, cap_ptr))?;
776 Ok(value)
777 }
778
779 /// Vertex connectivity of a pair of vertices.
780 ///
781 /// The minimum number of vertices whose deletion eliminates all paths
782 /// from `source` to `target` (directed paths in directed graphs). When
783 /// the two vertices are not adjacent this equals the number of internally
784 /// vertex-disjoint paths between them (Menger's theorem).
785 ///
786 /// Adjacent vertices cannot be separated by removing vertices; `neighbors`
787 /// decides what happens then:
788 /// [`VconnNei::Error`] fails with an error, [`VconnNei::Negative`] returns
789 /// −1, [`VconnNei::NumberOfNodes`] returns the number of vertices, and
790 /// [`VconnNei::Ignore`] ignores the direct edges and counts the vertices
791 /// needed to break every other path. This is why the result is signed.
792 ///
793 /// Time complexity: O(|V|³).
794 ///
795 /// See also [`Graph::vertex_disjoint_paths`] (which counts the direct
796 /// edges too) and [`Graph::is_separator`] to check a candidate vertex set.
797 ///
798 /// Binds [`igraph_st_vertex_connectivity`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_st_vertex_connectivity).
799 ///
800 /// # Errors
801 ///
802 /// [`ErrorKind::InvalidVertexId`] for invalid ids, [`ErrorKind::InvalidValue`]
803 /// if `source == target` or the vertices are adjacent and `neighbors` is
804 /// [`VconnNei::Error`].
805 ///
806 /// # Examples
807 ///
808 /// ```
809 /// use igraph::prelude::*;
810 ///
811 /// // In the complete graph K6, two adjacent vertices are joined by 4 other
812 /// // paths of length two.
813 /// let k6 = Graph::full(6, false, false)?;
814 /// assert_eq!(k6.st_vertex_connectivity(0, 1, VconnNei::Ignore)?, 4);
815 /// assert_eq!(k6.st_vertex_connectivity(0, 1, VconnNei::Negative)?, -1);
816 /// assert_eq!(k6.st_vertex_connectivity(0, 1, VconnNei::NumberOfNodes)?, 6);
817 /// assert!(k6.st_vertex_connectivity(0, 1, VconnNei::Error).is_err());
818 /// # Ok::<(), igraph::Error>(())
819 /// ```
820 pub fn st_vertex_connectivity(
821 &self,
822 source: VertexId,
823 target: VertexId,
824 neighbors: VconnNei,
825 ) -> Result<i64> {
826 check_pair(self, source, target)?;
827 let mut res = 0;
828 igraph_call!(igraph_st_vertex_connectivity(
829 self,
830 &mut res,
831 source,
832 target,
833 neighbors.into()
834 ))?;
835 Ok(res)
836 }
837
838 /// Vertex connectivity of the graph.
839 ///
840 /// The minimum of the vertex connectivity over all pairs of vertices,
841 /// i.e. the minimum number of vertices whose removal disconnects the
842 /// graph (the vertex count minus one for complete graphs). It coincides
843 /// with the *group cohesion* of White and Harary, see [`Graph::cohesion`].
844 ///
845 /// With `checks = true` igraph first performs cheap tests: a graph that is
846 /// not (strongly) connected has connectivity 0, a graph with a vertex of
847 /// degree 1 has connectivity 1, and a complete graph has `n - 1`. They
848 /// are recommended as the general computation is expensive, O(|V|⁵).
849 ///
850 /// See also [`Graph::articulation_points`] (a connected graph on at least
851 /// three vertices has vertex connectivity 1 exactly when it has one) and
852 /// [`Graph::minimum_size_separators`], which lists every vertex set of
853 /// this size whose removal disconnects the graph.
854 ///
855 /// Binds [`igraph_vertex_connectivity`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_vertex_connectivity).
856 ///
857 /// # Examples
858 ///
859 /// ```
860 /// use igraph::prelude::*;
861 ///
862 /// // A cycle survives the removal of any single vertex, but not of two.
863 /// let c = Graph::ring(5, false, false, true)?;
864 /// assert_eq!(c.vertex_connectivity(true)?, 2);
865 /// assert_eq!(c.vertex_connectivity(false)?, 2);
866 /// // The five minimum separators are the pairs of non-adjacent vertices.
867 /// assert_eq!(c.minimum_size_separators()?.len(), 5);
868 /// # Ok::<(), igraph::Error>(())
869 /// ```
870 pub fn vertex_connectivity(&self, checks: bool) -> Result<usize> {
871 let mut res = 0;
872 igraph_call!(igraph_vertex_connectivity(self, &mut res, checks))?;
873 Ok(count(res))
874 }
875
876 /// Edge connectivity of a pair of vertices.
877 ///
878 /// The minimum number of edges to delete in order to eliminate all paths
879 /// from `source` to `target` (directed paths in directed graphs); it is
880 /// the unit-capacity maximum flow between them, and equals
881 /// [`Graph::edge_disjoint_paths`].
882 ///
883 /// Time complexity: O(|V|³).
884 ///
885 /// Binds [`igraph_st_edge_connectivity`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_st_edge_connectivity).
886 ///
887 /// # Errors
888 ///
889 /// [`ErrorKind::InvalidVertexId`] for invalid ids, [`ErrorKind::InvalidValue`]
890 /// if `source == target`.
891 ///
892 /// # Examples
893 ///
894 /// ```
895 /// use igraph::prelude::*;
896 ///
897 /// // Two vertices of a 4-cycle are joined by two edge-disjoint routes.
898 /// let c = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false)?;
899 /// assert_eq!(c.st_edge_connectivity(0, 2)?, 2);
900 /// # Ok::<(), igraph::Error>(())
901 /// ```
902 pub fn st_edge_connectivity(&self, source: VertexId, target: VertexId) -> Result<usize> {
903 check_pair(self, source, target)?;
904 let mut res = 0;
905 igraph_call!(igraph_st_edge_connectivity(self, &mut res, source, target))?;
906 Ok(count(res))
907 }
908
909 /// Edge connectivity of the graph.
910 ///
911 /// The minimum of the edge connectivity over all pairs of vertices, i.e.
912 /// the minimum number of edges whose removal disconnects the graph. It is
913 /// the *group adhesion* of White and Harary, see [`Graph::adhesion`].
914 /// Graphs with at most one vertex have edge connectivity 0.
915 ///
916 /// With `checks = true` igraph first checks connectivity (0 if the graph
917 /// is not (strongly) connected) and minimum degree (1 if some vertex has
918 /// degree 1), which is much cheaper than the full computation.
919 ///
920 /// Time complexity: O(log|V| · |V|²) for undirected graphs, O(|V|⁴) for
921 /// directed graphs.
922 ///
923 /// See also [`Graph::bridges`] (a connected undirected graph has edge
924 /// connectivity 1 exactly when it has a bridge) and [`Graph::mincut`],
925 /// which also returns a cut realising the minimum.
926 ///
927 /// Binds [`igraph_edge_connectivity`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_edge_connectivity).
928 ///
929 /// # Examples
930 ///
931 /// ```
932 /// use igraph::prelude::*;
933 ///
934 /// // Two triangles sharing only vertex 2 ("bowtie"): no bridge, so edge
935 /// // connectivity 2, but vertex 2 is a cut vertex.
936 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3), (3, 4), (4, 2)], 5, false)?;
937 /// assert_eq!(g.edge_connectivity(true)?, 2);
938 /// assert_eq!(g.vertex_connectivity(true)?, 1);
939 /// assert!(g.bridges()?.is_empty());
940 /// assert_eq!(g.articulation_points()?, vec![2]);
941 /// # Ok::<(), igraph::Error>(())
942 /// ```
943 pub fn edge_connectivity(&self, checks: bool) -> Result<usize> {
944 let mut res = 0;
945 igraph_call!(igraph_edge_connectivity(self, &mut res, checks))?;
946 Ok(count(res))
947 }
948
949 /// Maximum number of edge-disjoint paths between two vertices.
950 ///
951 /// Paths are edge-disjoint when they share no edge; directed paths are
952 /// considered in directed graphs. The number equals the edge connectivity
953 /// of the pair (see [`Graph::st_edge_connectivity`]) and is computed with
954 /// maximum flows, in O(|V|³).
955 ///
956 /// Binds [`igraph_edge_disjoint_paths`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_edge_disjoint_paths).
957 ///
958 /// # Errors
959 ///
960 /// [`ErrorKind::InvalidVertexId`] for invalid ids,
961 /// [`ErrorKind::Unimplemented`] if `source == target`.
962 ///
963 /// # Examples
964 ///
965 /// ```
966 /// use igraph::prelude::*;
967 ///
968 /// // Bowtie: both triangles pass through vertex 2, yet two edge-disjoint
969 /// // routes exist from 0 to 3.
970 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3), (3, 4), (4, 2)], 5, false)?;
971 /// assert_eq!(g.edge_disjoint_paths(0, 3)?, 2);
972 /// assert_eq!(g.vertex_disjoint_paths(0, 3)?, 1);
973 /// # Ok::<(), igraph::Error>(())
974 /// ```
975 pub fn edge_disjoint_paths(&self, source: VertexId, target: VertexId) -> Result<usize> {
976 check_pair(self, source, target)?;
977 let mut res = 0;
978 igraph_call!(igraph_edge_disjoint_paths(self, &mut res, source, target))?;
979 Ok(count(res))
980 }
981
982 /// Maximum number of vertex-disjoint paths between two vertices.
983 ///
984 /// Paths are vertex-disjoint when they share no vertex other than their
985 /// endpoints; directed paths are considered in directed graphs. When
986 /// `source` and `target` are not adjacent this is their vertex
987 /// connectivity; every direct edge from `source` to `target` (either
988 /// orientation in undirected graphs, parallel edges counted separately)
989 /// contributes one extra path. Computed with maximum flows, O(|V|³).
990 ///
991 /// Binds [`igraph_vertex_disjoint_paths`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_vertex_disjoint_paths).
992 ///
993 /// # Errors
994 ///
995 /// [`ErrorKind::InvalidVertexId`] for invalid ids,
996 /// [`ErrorKind::Unimplemented`] if `source == target`.
997 ///
998 /// # Examples
999 ///
1000 /// ```
1001 /// use igraph::prelude::*;
1002 ///
1003 /// // Triangle: the direct edge plus the path through the third vertex.
1004 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false)?;
1005 /// assert_eq!(g.vertex_disjoint_paths(0, 1)?, 2);
1006 /// # Ok::<(), igraph::Error>(())
1007 /// ```
1008 pub fn vertex_disjoint_paths(&self, source: VertexId, target: VertexId) -> Result<usize> {
1009 check_pair(self, source, target)?;
1010 let mut res = 0;
1011 igraph_call!(igraph_vertex_disjoint_paths(self, &mut res, source, target))?;
1012 Ok(count(res))
1013 }
1014
1015 /// Graph adhesion: the edge connectivity with uniform edge weights.
1016 ///
1017 /// Defined by White and Harary (Sociological Methodology 31, 2001); it is
1018 /// the same as [`Graph::edge_connectivity`]. `checks` enables the same
1019 /// cheap connectivity/degree shortcuts.
1020 ///
1021 /// Time complexity: O(log|V| · |V|²) for undirected graphs, O(|V|⁴) for
1022 /// directed ones.
1023 ///
1024 /// Binds [`igraph_adhesion`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_adhesion).
1025 ///
1026 /// # Examples
1027 ///
1028 /// ```
1029 /// use igraph::prelude::*;
1030 ///
1031 /// // Every vertex of the Petersen graph has three neighbours, and no
1032 /// // smaller edge set disconnects it.
1033 /// let petersen = Graph::famous("Petersen")?;
1034 /// assert_eq!(petersen.adhesion(true)?, 3);
1035 /// assert_eq!(petersen.adhesion(true)?, petersen.edge_connectivity(false)?);
1036 /// # Ok::<(), igraph::Error>(())
1037 /// ```
1038 pub fn adhesion(&self, checks: bool) -> Result<usize> {
1039 let mut res = 0;
1040 igraph_call!(igraph_adhesion(self, &mut res, checks))?;
1041 Ok(count(res))
1042 }
1043
1044 /// Graph cohesion: the vertex connectivity of the graph.
1045 ///
1046 /// Defined by White and Harary (Sociological Methodology 31, 2001); it is
1047 /// the same as [`Graph::vertex_connectivity`]. `checks` enables the same
1048 /// cheap connectivity/degree/completeness shortcuts.
1049 ///
1050 /// Time complexity: O(|V|⁴), more like O(|V|²) in practice.
1051 ///
1052 /// See also [`Graph::cohesive_blocks`], the hierarchy of maximally
1053 /// cohesive subgraphs built on this measure.
1054 ///
1055 /// Binds [`igraph_cohesion`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_cohesion).
1056 ///
1057 /// # Examples
1058 ///
1059 /// ```
1060 /// use igraph::prelude::*;
1061 ///
1062 /// // The 3-dimensional cube is 3-connected in both senses.
1063 /// let cube = Graph::hypercube(3, false)?;
1064 /// assert_eq!(cube.cohesion(true)?, 3);
1065 /// assert_eq!(cube.adhesion(true)?, 3);
1066 /// # Ok::<(), igraph::Error>(())
1067 /// ```
1068 pub fn cohesion(&self, checks: bool) -> Result<usize> {
1069 let mut res = 0;
1070 igraph_call!(igraph_cohesion(self, &mut res, checks))?;
1071 Ok(count(res))
1072 }
1073
1074 /// Even–Tarjan reduction: turns vertex cuts into edge cuts.
1075 ///
1076 /// Builds a directed graph with `2 n` vertices: each vertex `i` becomes
1077 /// `i' = i` and `i'' = i + n`, joined by an edge `i' → i''` (these come
1078 /// first, capacity 1). Each original edge `(i, j)` becomes the two edges
1079 /// `i'' → j'` and `j'' → i'` (capacity `n`, standing for infinity). A
1080 /// minimum `i'' → j'` cut in the reduced graph then corresponds to a
1081 /// minimum vertex separator of `i` and `j` in the original graph (Even and
1082 /// Tarjan, SIAM J. Comput. 4(4), 1975; Kanevsky, Networks 23, 1993).
1083 ///
1084 /// Directedness of the input is not checked; the reduction is normally
1085 /// applied to directed graphs.
1086 ///
1087 /// Time complexity: O(|V| + |E|).
1088 ///
1089 /// Binds [`igraph_even_tarjan_reduction`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_even_tarjan_reduction).
1090 ///
1091 /// # Examples
1092 ///
1093 /// ```
1094 /// use igraph::prelude::*;
1095 ///
1096 /// // Path 0 - 1 - 2: vertex 1 separates 0 from 2.
1097 /// let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, false)?;
1098 /// let et = g.even_tarjan_reduction()?;
1099 /// assert_eq!((et.graph.vcount(), et.graph.ecount()), (6, 7));
1100 /// assert_eq!(et.capacity, vec![1.0, 1.0, 1.0, 3.0, 3.0, 3.0, 3.0]);
1101 /// // Flow from 0'' (= 3) to 2' (= 2): one vertex must be removed.
1102 /// assert_eq!(et.graph.maxflow_value(3, 2, Some(&et.capacity))?, 1.0);
1103 /// # Ok::<(), igraph::Error>(())
1104 /// ```
1105 pub fn even_tarjan_reduction(&self) -> Result<EvenTarjanReduction> {
1106 let mut capacity = Vector::new();
1107 let graph =
1108 Graph::init_with(|g| unsafe { igraph_even_tarjan_reduction(self, g, &mut capacity) })?;
1109 Ok(EvenTarjanReduction {
1110 graph,
1111 capacity: capacity.into(),
1112 })
1113 }
1114
1115 /// Residual graph of a flow.
1116 ///
1117 /// Given the edge `capacity` and a `flow` on each edge (e.g.
1118 /// [`MaxFlow::flow`] of a directed graph), returns the directed graph
1119 /// containing, in edge-id order, every edge whose flow is strictly below
1120 /// its capacity, together with its residual capacity
1121 /// `capacity - flow`. The vertex set is unchanged.
1122 ///
1123 /// Binds `igraph_residual_graph` (undocumented in the C reference manual,
1124 /// see [the Flows chapter](https://igraph.org/c/html/latest/igraph-Flows.html)).
1125 ///
1126 /// # Errors
1127 ///
1128 /// [`ErrorKind::InvalidValue`] if `capacity` or `flow` do not have one
1129 /// entry per edge, or if a capacity is negative, NaN or infinite.
1130 ///
1131 /// # Examples
1132 ///
1133 /// ```
1134 /// use igraph::prelude::*;
1135 ///
1136 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true)?;
1137 /// let capacity = [2.0, 1.0, 1.0];
1138 /// let mf = g.maxflow(0, 2, Some(&capacity))?;
1139 /// assert_eq!(mf.flow, vec![1.0, 1.0, 1.0]);
1140 /// let res = g.residual_graph(&capacity, &mf.flow)?;
1141 /// // Only 0 -> 1 is not saturated.
1142 /// assert_eq!(res.graph.edge_list(), vec![(0, 1)]);
1143 /// assert_eq!(res.capacity, vec![1.0]);
1144 /// # Ok::<(), igraph::Error>(())
1145 /// ```
1146 pub fn residual_graph(&self, capacity: &[f64], flow: &[f64]) -> Result<ResidualGraph> {
1147 check_capacity(self, Some(capacity))?;
1148 check_len("flow", flow, self.ecount())?;
1149 let cap = Vector::view(capacity);
1150 let fl = Vector::view(flow);
1151 let mut residual_capacity = Vector::new();
1152 let graph = Graph::init_with(|g| unsafe {
1153 igraph_residual_graph(self, cap.as_ptr(), g, &mut residual_capacity, fl.as_ptr())
1154 })?;
1155 Ok(ResidualGraph {
1156 graph,
1157 capacity: residual_capacity.into(),
1158 })
1159 }
1160
1161 /// Reverse residual graph of a flow.
1162 ///
1163 /// For every edge `u → v` of the input, in edge-id order, the result
1164 /// contains `u → v` if the edge carries a positive flow, and `v → u` if
1165 /// its flow is below its capacity. This is the graph used by
1166 /// [`Graph::all_st_mincuts`] to enumerate the minimum cuts.
1167 /// `capacity: None` means unit capacities.
1168 ///
1169 /// Binds `igraph_reverse_residual_graph` (undocumented in the C reference
1170 /// manual, see [the Flows chapter](https://igraph.org/c/html/latest/igraph-Flows.html)).
1171 ///
1172 /// # Errors
1173 ///
1174 /// [`ErrorKind::InvalidValue`] if `capacity` or `flow` do not have one
1175 /// entry per edge, or if a capacity is negative, NaN or infinite.
1176 ///
1177 /// # Examples
1178 ///
1179 /// ```
1180 /// use igraph::prelude::*;
1181 ///
1182 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true)?;
1183 /// let rr = g.reverse_residual_graph(Some(&[2.0, 1.0, 1.0]), &[1.0, 1.0, 1.0])?;
1184 /// assert_eq!(rr.edge_list(), vec![(0, 1), (1, 0), (1, 2), (0, 2)]);
1185 /// # Ok::<(), igraph::Error>(())
1186 /// ```
1187 pub fn reverse_residual_graph(&self, capacity: Option<&[f64]>, flow: &[f64]) -> Result<Graph> {
1188 check_capacity(self, capacity)?;
1189 check_len("flow", flow, self.ecount())?;
1190 let cap = capacity.map(Vector::view);
1191 let cap_ptr = cap.as_ref().map_or(std::ptr::null(), |v| v.as_ptr());
1192 let fl = Vector::view(flow);
1193 Graph::init_with(|g| unsafe {
1194 igraph_reverse_residual_graph(self, cap_ptr, g, fl.as_ptr())
1195 })
1196 }
1197
1198 /// Dominator tree of a flowgraph rooted at `root`.
1199 ///
1200 /// In a directed graph where every vertex is reachable from `root`, a
1201 /// vertex `v` *dominates* `w ≠ v` if every path from the root to `w`
1202 /// passes through `v`. The *immediate dominator* `idom(w)` is the
1203 /// dominator of `w` dominated by all its other dominators; the edges
1204 /// `idom(w) → w` form a tree rooted at `root`, and `v` dominates `w` iff
1205 /// `v` is an ancestor of `w` in it. Implemented with the Lengauer–Tarjan
1206 /// algorithm (ACM TOPLAS 1, 1979), in O(|V| + |E| α(|E|, |V|)).
1207 ///
1208 /// `mode` must be [`NeighborMode::Out`] or [`NeighborMode::In`]; with
1209 /// `In` all edges are followed backwards (post-dominators). Vertices not
1210 /// reachable from the root are reported in
1211 /// [`DominatorTree::leftout`] and are isolated in the tree.
1212 ///
1213 /// See also [`Graph::subcomponent`] for plain reachability from the root.
1214 ///
1215 /// Binds [`igraph_dominator_tree`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_dominator_tree).
1216 ///
1217 /// # Errors
1218 ///
1219 /// [`ErrorKind::InvalidVertexId`] for an invalid root,
1220 /// [`ErrorKind::InvalidValue`] for undirected graphs or
1221 /// `mode == NeighborMode::All`.
1222 ///
1223 /// # Examples
1224 ///
1225 /// The example of igraph's `dominator_tree.c`:
1226 ///
1227 /// ```
1228 /// use igraph::prelude::*;
1229 ///
1230 /// let g = Graph::from_edges(&[
1231 /// (0, 9), (1, 0), (1, 2), (2, 3), (2, 7), (3, 1), (4, 1), (4, 3),
1232 /// (5, 2), (5, 3), (5, 4), (5, 8), (6, 5), (6, 9), (8, 7),
1233 /// ], 10, true)?;
1234 /// let dt = g.dominator_tree(9, NeighborMode::In)?;
1235 /// assert_eq!(dt.dom[..3], [Some(9), Some(0), Some(3)]);
1236 /// assert_eq!(dt.dom[9], None); // the root
1237 /// assert_eq!(dt.leftout, vec![7, 8]);
1238 /// assert_eq!(dt.dominators(2), vec![3, 1, 0, 9]);
1239 /// # Ok::<(), igraph::Error>(())
1240 /// ```
1241 pub fn dominator_tree(&self, root: VertexId, mode: NeighborMode) -> Result<DominatorTree> {
1242 check_vertex(self, root, "root")?;
1243 let mut dom = VectorInt::new();
1244 let mut leftout = VectorInt::new();
1245 let tree = Graph::init_with(|g| unsafe {
1246 igraph_dominator_tree(self, root, &mut dom, g, &mut leftout, mode.into())
1247 })?;
1248 Ok(DominatorTree {
1249 dom: dom.iter().map(|&d| (d >= 0).then_some(d)).collect(),
1250 tree,
1251 leftout: leftout.into(),
1252 })
1253 }
1254
1255 /// Lists all minimal edge cuts between `source` and `target` in a
1256 /// directed graph.
1257 ///
1258 /// Following Provan and Shier (Algorithmica 15, 1996), an s-t cut here is
1259 /// a *minimal* set of edges whose removal leaves no directed path from
1260 /// `source` to `target`: supersets of a cut are not listed (on the path
1261 /// `0 → 1 → 2` the cuts are `{0 → 1}` and `{1 → 2}`, not both edges
1262 /// together). Every such cut is listed exactly once, both as its set of
1263 /// edges and as the vertex set `X ∋ source` generating it (the cut is the
1264 /// set of edges leaving `X`). Runs in O(n (|V| + |E|)) where n is the
1265 /// number of cuts — which can be exponential in the size of the graph.
1266 ///
1267 /// When `target` is not reachable from `source`, or `source == target`,
1268 /// igraph lists no cuts at all (the result is empty, not an error).
1269 ///
1270 /// Binds [`igraph_all_st_cuts`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_all_st_cuts).
1271 ///
1272 /// # Errors
1273 ///
1274 /// [`ErrorKind::Unimplemented`] for undirected graphs,
1275 /// [`ErrorKind::InvalidVertexId`] for invalid ids.
1276 ///
1277 /// # Examples
1278 ///
1279 /// ```
1280 /// use igraph::prelude::*;
1281 ///
1282 /// // On a directed path, each edge alone is a cut.
1283 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, true)?;
1284 /// let all = g.all_st_cuts(0, 3)?;
1285 /// assert_eq!(all.cuts, vec![vec![0], vec![1], vec![2]]);
1286 /// assert_eq!(all.partition1s, vec![vec![0], vec![0, 1], vec![0, 1, 2]]);
1287 /// # Ok::<(), igraph::Error>(())
1288 /// ```
1289 pub fn all_st_cuts(&self, source: VertexId, target: VertexId) -> Result<StCuts> {
1290 check_pair(self, source, target)?;
1291 let mut cuts = VectorIntList::new();
1292 let mut partition1s = VectorIntList::new();
1293 igraph_call!(igraph_all_st_cuts(
1294 self,
1295 &mut cuts,
1296 &mut partition1s,
1297 source,
1298 target
1299 ))?;
1300 Ok(StCuts {
1301 cuts: cuts.into(),
1302 partition1s: partition1s.into(),
1303 })
1304 }
1305
1306 /// Lists all minimum-capacity edge cuts between `source` and `target` in
1307 /// a directed graph.
1308 ///
1309 /// Several cuts may share the minimum total capacity. Each is returned as
1310 /// its edge set and as the generating vertex set `X ∋ source`. Capacities
1311 /// must be strictly positive (`None` means unit capacities); integer
1312 /// capacities are recommended, as round-off may hide some cuts otherwise.
1313 /// Uses a maximum flow followed by the Provan–Shier enumeration on the
1314 /// [reverse residual graph](Graph::reverse_residual_graph). When `target`
1315 /// is not reachable from `source` the value is 0 and no cut is listed.
1316 ///
1317 /// Binds [`igraph_all_st_mincuts`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_all_st_mincuts).
1318 ///
1319 /// # Errors
1320 ///
1321 /// [`ErrorKind::Unimplemented`] for undirected graphs,
1322 /// [`ErrorKind::InvalidVertexId`] for invalid ids,
1323 /// [`ErrorKind::InvalidValue`] if `source == target`, or for a capacity
1324 /// vector of the wrong length or with non-positive or non-finite entries.
1325 ///
1326 /// # Examples
1327 ///
1328 /// ```
1329 /// use igraph::prelude::*;
1330 ///
1331 /// // Two parallel branches 1->2->4 and 1->3->4 between a single entry
1332 /// // edge 0->1 and a single exit edge 4->5.
1333 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (1, 3), (2, 4), (3, 4), (4, 5)], 6, true)?;
1334 /// let m = g.all_st_mincuts(0, 5, None)?;
1335 /// assert_eq!(m.value, 1.0);
1336 /// assert_eq!(m.cuts, vec![vec![0], vec![5]]);
1337 /// # Ok::<(), igraph::Error>(())
1338 /// ```
1339 pub fn all_st_mincuts(
1340 &self,
1341 source: VertexId,
1342 target: VertexId,
1343 capacity: Option<&[f64]>,
1344 ) -> Result<StMinCuts> {
1345 check_capacity(self, capacity)?;
1346 check_pair(self, source, target)?;
1347 let cap = capacity.map(Vector::view);
1348 let cap_ptr = cap.as_ref().map_or(std::ptr::null(), |v| v.as_ptr());
1349 let mut value = 0.0;
1350 let mut cuts = VectorIntList::new();
1351 let mut partition1s = VectorIntList::new();
1352 igraph_call!(igraph_all_st_mincuts(
1353 self,
1354 &mut value,
1355 &mut cuts,
1356 &mut partition1s,
1357 source,
1358 target,
1359 cap_ptr,
1360 ))?;
1361 Ok(StMinCuts {
1362 value,
1363 cuts: cuts.into(),
1364 partition1s: partition1s.into(),
1365 })
1366 }
1367
1368 /// Gomory–Hu tree of an undirected graph.
1369 ///
1370 /// A tree on the same vertices whose edges are annotated with flow values
1371 /// such that, for every pair `(u, v)`, the maximum flow (minimum cut)
1372 /// between `u` and `v` in the original graph is the minimum annotation
1373 /// along the tree path from `u` to `v` (see
1374 /// [`GomoryHuTree::flow_between`]). All `n (n - 1) / 2` pairwise flows are
1375 /// thus encoded by `n - 1` numbers. Built with Gusfield's algorithm (SIAM
1376 /// J. Comput. 19(1), 1990) using `n - 1` max-flow computations, O(|V|⁴).
1377 /// `capacity: None` means unit capacities. The smallest annotation is the
1378 /// global minimum cut value ([`Graph::mincut_value`]).
1379 ///
1380 /// Binds [`igraph_gomory_hu_tree`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_gomory_hu_tree).
1381 ///
1382 /// # Errors
1383 ///
1384 /// [`ErrorKind::InvalidValue`] for directed graphs or a capacity vector
1385 /// of the wrong length or with a negative, NaN or infinite entry.
1386 ///
1387 /// # Examples
1388 ///
1389 /// ```
1390 /// use igraph::prelude::*;
1391 ///
1392 /// let g = Graph::from_edges(&[(0, 1), (0, 2), (1, 2), (2, 3)], 4, false)?;
1393 /// let gh = g.gomory_hu_tree(None)?;
1394 /// assert_eq!(gh.tree.ecount(), 3);
1395 /// assert_eq!(gh.flow_between(0, 1), Some(2.0)); // inside the triangle
1396 /// assert_eq!(gh.flow_between(0, 3), Some(1.0)); // through the pendant edge
1397 /// # Ok::<(), igraph::Error>(())
1398 /// ```
1399 pub fn gomory_hu_tree(&self, capacity: Option<&[f64]>) -> Result<GomoryHuTree> {
1400 check_capacity(self, capacity)?;
1401 let cap = capacity.map(Vector::view);
1402 let cap_ptr = cap.as_ref().map_or(std::ptr::null(), |v| v.as_ptr());
1403 let mut flows = Vector::new();
1404 let tree =
1405 Graph::init_with(|g| unsafe { igraph_gomory_hu_tree(self, g, &mut flows, cap_ptr) })?;
1406 Ok(GomoryHuTree {
1407 tree,
1408 flows: flows.into(),
1409 })
1410 }
1411}