Skip to main content

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}