Global minimum cut algorithms for undirected graphs.
This module finds the global minimum cut in an undirected weighted graph - the partition of nodes into two non-empty sets that minimizes the total weight of edges crossing between the sets.
Algorithm Summary
| Algorithm | Function | Complexity | Best For |
|---|---|---|---|
| Stoer-Wagner | global_min_cut/2 | O(V³) | Dense undirected graphs, exact result |
| Karger-Stein | karger_stein/2 | O(V² log³ V) | Large graphs, randomized approach |
| Gomory-Hu Tree | gomory_hu_tree/1 | O(V · MaxFlow) | All-pairs min-cut analysis |
Key Concepts
- Global Min-Cut: The minimum cut over all possible partitions of the graph.
- s-t Cut: A cut that separates specific nodes
sandt. - Maximum Adjacency Search: Orders vertices by strength of connection to the current set (used by Stoer-Wagner).
- Edge Contraction: Merging two nodes while preserving total edge weight to other nodes (used by Karger-Stein).
Gomory-Hu Trees
The Gomory-Hu tree is a powerful structure that encodes all-pairs min-cuts using only V-1 max-flow
computations. In the tree, the min-cut value between any two nodes u and v is the minimum
weight on the unique tree path between them.
Use gomory_hu_tree/1 to build the tree and min_cut_query/3 to extract values and partitions.
Randomized Algorithms
For very large graphs where exact algorithms (O(V³)) might be slow, karger_stein/2
provides a randomized approach based on edge contraction. It uses recursive branching
to achieve a high probability of success.
Comparison with s-t Min-Cut
For finding a cut between specific source and sink nodes, use Yog.Flow.MaxFlow or the convenience s_t_min_cut/4:
s_t_min_cut(graph, s, t, :dinic): Efficiently finds the cut separatingsandt.global_min_cut(graph): Finds the minimum weight cut across any possible partition.
Examples
Global Minimum Cut (Stoer-Wagner)
iex> graph = Yog.from_edges(:undirected, [{1, 2, 5}, {2, 3, 3}, {1, 3, 2}])
iex> result = Yog.Flow.MinCut.global_min_cut(graph)
iex> result.cut_value
5All-Pairs Analysis (Gomory-Hu)
iex> graph = Yog.from_edges(:undirected, [{1, 2, 3}, {1, 3, 4}, {2, 3, 2}, {2, 4, 5}, {3, 4, 1}])
iex> tree = Yog.Flow.MinCut.gomory_hu_tree(graph)
iex> {cut_value, _s, _t} = Yog.Flow.MinCut.min_cut_query(tree, 1, 4)
iex> cut_value
6References
Summary
Functions
Finds the global minimum cut of an undirected weighted graph.
Builds a Gomory-Hu tree representing all-pairs min-cuts in the graph.
Finds the global minimum cut using the randomized Karger-Stein algorithm.
Queries the min-cut value and partitions between two nodes using a Gomory-Hu tree.
Computes the minimum s-t cut using a max-flow algorithm.
Functions
@spec global_min_cut( Yog.graph(), keyword() ) :: Yog.Flow.MinCutResult.t()
Finds the global minimum cut of an undirected weighted graph.
This implementation uses the Stoer-Wagner algorithm. It repeatedly finds an s-t min-cut in a phase using Maximum Adjacency Search and then contracts the nodes s and t. The global minimum cut is the minimum of all phase cuts.
Time Complexity: O(V³) (O(V² log V) with priority queue optimization)
Examples
Simple triangle graph:
iex> {:ok, graph} = Yog.undirected()
...> |> Yog.add_node(1, "A")
...> |> Yog.add_node(2, "B")
...> |> Yog.add_node(3, "C")
...> |> Yog.add_edges([{1, 2, 5}, {2, 3, 3}, {1, 3, 2}])
iex> result = Yog.Flow.MinCut.global_min_cut(graph)
iex> result.cut_value
5
iex> result.source_side_size + result.sink_side_size
3Parameters
graph- The undirected weighted graphopts- Options::track_partitions- Iftrue, populates thesource_sideandsink_sidefields in the result. (default:false)
Notes
- The graph must be undirected
- Edge weights must be integers
- Returns a
Yog.Flow.MinCutResultstruct
Builds a Gomory-Hu tree representing all-pairs min-cuts in the graph.
The Gomory-Hu tree is a weighted tree on the same vertex set where the
minimum edge weight on the path between any two nodes equals their min-cut
value in the original graph. This implementation uses Gusfield's algorithm,
requiring only V-1 max-flow computations.
Examples
iex> graph = Yog.from_edges(:undirected, [{1, 2, 3}, {1, 3, 4}, {2, 3, 2}, {2, 4, 5}, {3, 4, 1}])
iex> tree = Yog.Flow.MinCut.gomory_hu_tree(graph)
iex> length(Yog.Model.all_edges(tree))
3
@spec karger_stein( Yog.graph(), keyword() ) :: Yog.Flow.MinCutResult.t()
Finds the global minimum cut using the randomized Karger-Stein algorithm.
Karger-Stein uses repeated random edge contraction with recursive branching to achieve high success probability. It is particularly effective on large graphs where the exact Stoer-Wagner algorithm may be slower, as the randomized approach can often find the global minimum cut with very few iterations.
Parameters
graph- The undirected weighted graphopts- Options::iterations- Number of independent runs of the recursive fast-cut procedure (default:max(1, trunc(n * log2(n + 1)))). Increasing the number of iterations improves the success probability.
Examples
iex> graph = Yog.from_edges(:undirected, [{1, 2, 5}, {2, 3, 3}, {1, 3, 2}])
iex> result = Yog.Flow.MinCut.karger_stein(graph)
iex> result.cut_value
5
@spec min_cut_query(Yog.graph(), Yog.node_id(), Yog.node_id()) :: {number(), MapSet.t(Yog.node_id()), MapSet.t(Yog.node_id())}
Queries the min-cut value and partitions between two nodes using a Gomory-Hu tree.
Returns {cut_value, partition_s, partition_t} where cut_value is the min-cut
between node_a and node_b in the original graph, and the partitions are the
two sides of that cut.
Examples
iex> graph = Yog.from_edges(:undirected, [{1, 2, 3}, {1, 3, 4}, {2, 3, 2}, {2, 4, 5}, {3, 4, 1}])
iex> tree = Yog.Flow.MinCut.gomory_hu_tree(graph)
iex> {cut_value, _s, _t} = Yog.Flow.MinCut.min_cut_query(tree, 1, 4)
iex> cut_value
6
@spec s_t_min_cut(Yog.graph(), Yog.node_id(), Yog.node_id(), atom()) :: Yog.Flow.MinCutResult.t()
Computes the minimum s-t cut using a max-flow algorithm.
This is a convenience wrapper that delegates to Yog.Flow.MaxFlow.max_flow/4
and extracts the corresponding min-cut from the residual graph.
Parameters
graph- The flow networksource- Source node IDsink- Sink node IDalgorithm- Algorithm to use::edmonds_karp(default),:dinic, or:push_relabel
Examples
iex> {:ok, graph} = Yog.directed()
...> |> Yog.add_node(1, "s")
...> |> Yog.add_node(2, "a")
...> |> Yog.add_node(3, "t")
...> |> Yog.add_edges([{1, 2, 10}, {2, 3, 5}])
iex> result = Yog.Flow.MinCut.s_t_min_cut(graph, 1, 3)
iex> result.cut_value
5