diff --git a/doc/weights.xml b/doc/weights.xml
index 22c6b71e9..7d918606d 100644
--- a/doc/weights.xml
+++ b/doc/weights.xml
@@ -272,6 +272,64 @@ gap> Sum(flow[1]);
<#/GAPDoc>
+<#GAPDoc Label="DigraphMinimumCut">
+
+
+ A list of lists of integers.
+
+ If digraph is an edge-weighted digraph with distinct vertices s
+ and t, this function returns a list of two lists representing the
+ components of the minimum s-t cut of digraph.
+
+ An s-t cut is a partition of the vertices \{ S, T \}
+ such that s is in S and t is in T. The capacity
+ of an s-t cut is the sum of the weights of every edge whose source
+ is in S and whose range is in T. The minimum s-t cut is
+ the s-t cut whose capacity is the smallest possible.
+
+ This attribute is computed by using and the
+ max-cut min-flow theorem.
+
+ See .
+ g := EdgeWeightedDigraph([[2, 2], [3], []], [[3, 2], [1], []]);
+
+gap> DigraphMinimumCut(g, 1, 3);
+[ [ 1, 2 ], [3] ]
+]]>
+
+
+<#/GAPDoc>
+
+<#GAPDoc Label="DigraphMinimumCutSet">
+
+
+ A list of lists integers.
+
+ If digraph is an edge-weighted digraph with distinct vertices s
+ and t, this function returns a list of lists of integers representing the
+ minimum s-t cut of digraph.
+
+ An s-t cut is a partition of the vertices \{ S, T \}
+ such that s is in S and t is in T. The cut set
+ corresponding to this cut is the set of edges whose source is in S and
+ whose range is in T. The minimum s-t cut set is
+ the s-t cut set such that the sum of the weights of the edges in
+ the cut set is the smallest possible.
+
+ This attribute is computed by using .
+
+ See .
+ g := EdgeWeightedDigraph([[2, 2], [3], []], [[3, 2], [1], []]);
+
+gap> DigraphMinimumCutSet(g, 1, 3);
+[ [ 2, 3 ] ]
+]]>
+
+
+<#/GAPDoc>
+
<#GAPDoc Label="RandomUniqueEdgeWeightedDigraph">
diff --git a/doc/z-chap5.xml b/doc/z-chap5.xml
index 759876e11..21576bc93 100644
--- a/doc/z-chap5.xml
+++ b/doc/z-chap5.xml
@@ -32,6 +32,8 @@
<#Include Label="EdgeWeightedDigraphShortestPaths">
<#Include Label="EdgeWeightedDigraphShortestPath">
<#Include Label="DigraphMaximumFlow">
+ <#Include Label="DigraphMinimumCut">
+ <#Include Label="DigraphMinimumCutSet">
<#Include Label="RandomUniqueEdgeWeightedDigraph">
diff --git a/gap/weights.gd b/gap/weights.gd
index 7d11bf5f7..25991e48b 100644
--- a/gap/weights.gd
+++ b/gap/weights.gd
@@ -35,9 +35,13 @@ DeclareGlobalFunction("DIGRAPHS_Edge_Weighted_FloydWarshall");
DeclareGlobalFunction("DIGRAPHS_Edge_Weighted_Bellman_Ford");
DeclareGlobalFunction("DIGRAPHS_Edge_Weighted_Dijkstra");
-# 5. Maximum Flow
+# 5. Maximum Flow and Minimum Cut
DeclareOperation("DigraphMaximumFlow",
[IsDigraph and HasEdgeWeights, IsPosInt, IsPosInt]);
+DeclareOperation("DigraphMinimumCut",
+ [IsDigraph and HasEdgeWeights, IsPosInt, IsPosInt]);
+DeclareOperation("DigraphMinimumCutSet",
+ [IsDigraph and HasEdgeWeights, IsPosInt, IsPosInt]);
# 6. Random edge weighted digraphs
DeclareOperation("RandomUniqueEdgeWeightedDigraph", [IsPosInt]);
diff --git a/gap/weights.gi b/gap/weights.gi
index 5b246b002..b55cf13e0 100644
--- a/gap/weights.gi
+++ b/gap/weights.gi
@@ -638,7 +638,7 @@ function(D, source)
end);
#############################################################################
-# 5. Maximum Flow
+# 5. Maximum Flow and Minimum Cut
#############################################################################
InstallMethod(DigraphMaximumFlow, "for an edge weighted digraph",
@@ -772,6 +772,87 @@ function(D, start, destination)
return flows;
end);
+InstallMethod(DigraphMinimumCut, "for an edge weighted digraph",
+[IsDigraph and HasEdgeWeights, IsPosInt, IsPosInt],
+function(D, s, t)
+ local weights, outs, vertices, flow, residuals, u, v, S, T, seen, i;
+
+ # Extract important data
+ weights := EdgeWeights(D);
+ outs := OutNeighbours(D);
+ vertices := DigraphVertices(D);
+
+ # Check input
+ if s < 1 or s > Length(vertices) then
+ ErrorNoReturn(" must be a vertex of ,");
+ elif t < 1 or t > Length(vertices) then
+ ErrorNoReturn(" must be a vertex of ,");
+ elif s = t then
+ ErrorNoReturn(" and must be distinct");
+ fi;
+
+ # Find the residual edge capacities under the maximum flow
+ flow := DigraphMaximumFlow(D, s, t);
+ residuals := weights - flow;
+
+ # Carry out a BFS to find all the vertices in the residual
+ # network which are reachable from s. This gives the minimum
+ # cut by the max-flow min-cut theorem.
+
+ S := [s];
+ seen := BlistList(DigraphVertices(D), [s]);
+ i := 1;
+ while i <= Length(S) do
+ u := S[i];
+ i := i + 1;
+ for v in [1 .. Length(outs[u])] do
+ if residuals[u][v] > 0 then
+ if not seen[outs[u][v]] then
+ Add(S, outs[u][v]);
+ seen[outs[u][v]] := true;
+ fi;
+ fi;
+ od;
+ od;
+
+ T := Difference(vertices, S);
+ return [S, T];
+end);
+
+InstallMethod(DigraphMinimumCutSet, "for an edge weighted digraph",
+[IsDigraph and HasEdgeWeights, IsPosInt, IsPosInt],
+function(D, s, t)
+ local cut, cutset, u, v, S, T;
+
+ # Check input
+ if s < 1 or s > DigraphNrVertices(D) then
+ ErrorNoReturn(" must be a vertex of ,");
+ elif t < 1 or t > DigraphNrVertices(D) then
+ ErrorNoReturn(" must be a vertex of ,");
+ elif s = t then
+ ErrorNoReturn(" and must be distinct");
+ fi;
+
+ # Get the minimum cut
+
+ cut := DigraphMinimumCut(D, s, t);
+ S := cut[1];
+ T := cut[2];
+
+ # Compute the cut-set corresponding to the minimum cut
+
+ cutset := [];
+ for u in S do
+ for v in OutNeighbours(D)[u] do
+ if v in T then
+ Add(cutset, [u, v]);
+ fi;
+ od;
+ od;
+
+ return cutset;
+end);
+
#############################################################################
# 6. Random edge weighted digraphs
#############################################################################
diff --git a/tst/standard/weights.tst b/tst/standard/weights.tst
index 59a632140..739a12a70 100644
--- a/tst/standard/weights.tst
+++ b/tst/standard/weights.tst
@@ -291,7 +291,7 @@ gap> EdgeWeightedDigraphShortestPath(d, 1, 3);
[ [ 1, 2, 3 ], [ 1, 1 ] ]
#############################################################################
-# 5. Maximum Flow
+# 5. Maximum Flow and Minimum Cut
#############################################################################
# Maximum flow: empty digraphs
@@ -368,6 +368,110 @@ gap> gr := EdgeWeightedDigraph([[2], [3, 6], [4], [1, 6], [1, 3], []],
gap> DigraphMaximumFlow(gr, 5, 6);
[ [ 10 ], [ 3, 7 ], [ 7 ], [ 0, 7 ], [ 10, 4 ], [ ] ]
+# Minimum cut: empty digraphs
+gap> d := EdgeWeightedDigraph([], []);
+
+gap> DigraphMinimumCut(d, 1, 1);
+Error, must be a vertex of ,
+
+# Minimum cut: single vertex (also empty digraphs)
+gap> d := EdgeWeightedDigraph([[]], [[]]);
+
+gap> DigraphMinimumCut(d, 1, 1);
+Error, and must be distinct
+
+# Minimum cut: source = dest
+gap> d := EdgeWeightedDigraph([[2], []], [[5], []]);
+
+gap> DigraphMinimumCut(d, 1, 1);
+Error, and must be distinct
+
+# Minimum cut: has loop
+gap> d := EdgeWeightedDigraph([[1, 2], []], [[5, 10], []]);
+
+gap> DigraphMinimumCut(d, 1, 2);
+[ [ 1 ], [ 2 ] ]
+
+# Minimum cut: invalid source
+gap> d := EdgeWeightedDigraph([[1, 2], []], [[5, 10], []]);
+
+gap> DigraphMinimumCut(d, 5, 2);
+Error, must be a vertex of ,
+
+# Minimum cut: invalid sink
+gap> d := EdgeWeightedDigraph([[1, 2], []], [[5, 10], []]);
+
+gap> DigraphMinimumCut(d, 1, 5);
+Error, must be a vertex of ,
+
+# Minimum cut: sink not reachable
+gap> d := EdgeWeightedDigraph([[1], []], [[5], []]);
+
+gap> DigraphMinimumCut(d, 1, 2);
+[ [ 1 ], [ 2 ] ]
+
+# Minimum cut: source has in neighbours
+gap> d := EdgeWeightedDigraph([[2], [3], []], [[5], [10], []]);
+
+gap> DigraphMinimumCut(d, 2, 3);
+[ [ 2 ], [ 1, 3 ] ]
+
+# Minimum cut: sink has out-neighbours
+gap> d := EdgeWeightedDigraph([[2], [3], [2]], [[5], [10], [7]]);
+
+gap> DigraphMinimumCut(d, 2, 3);
+[ [ 2 ], [ 1, 3 ] ]
+
+# Minimum cut: cycle
+gap> d := EdgeWeightedDigraph([[2], [3], [1]], [[5], [10], [7]]);
+
+gap> DigraphMinimumCut(d, 1, 3);
+[ [ 1 ], [ 2, 3 ] ]
+
+# Minimum cut: example from Wikipedia
+gap> gr := EdgeWeightedDigraph([[3, 4], [], [2, 4], [2]],
+> [[10, 5], [], [5, 15], [10]]);;
+gap> DigraphMinimumCut(gr, 1, 2);
+[ [ 1 ], [ 2, 3, 4 ] ]
+gap> DigraphMinimumCut(gr, 3, 2);
+[ [ 3, 4 ], [ 1, 2 ] ]
+
+# Minimum cut: example from Wikipedia article on Push-label maximum flow
+gap> gr := EdgeWeightedDigraph([[2], [3, 6], [4], [1, 6], [1, 3], []],
+> [[12], [3, 7], [10], [5, 10], [15, 4], []]);;
+gap> DigraphMinimumCut(gr, 5, 6);
+[ [ 5, 1, 2 ], [ 3, 4, 6 ] ]
+
+# Minimum cut set: empty digraphs
+gap> d := EdgeWeightedDigraph([], []);
+
+gap> DigraphMinimumCutSet(d, 1, 1);
+Error, must be a vertex of ,
+
+# Minimum cut set: source has in neighbours
+gap> d := EdgeWeightedDigraph([[2], [3], []], [[5], [10], []]);
+
+gap> DigraphMinimumCutSet(d, 2, 3);
+[ [ 2, 3 ] ]
+
+# Minimum cut set: cycle
+gap> d := EdgeWeightedDigraph([[2], [3], [1]], [[5], [10], [7]]);
+
+gap> DigraphMinimumCutSet(d, 1, 3);
+[ [ 1, 2 ] ]
+
+# Minimum cut set: invalid sink
+gap> d := EdgeWeightedDigraph([[1, 2], []], [[5, 10], []]);
+
+gap> DigraphMinimumCutSet(d, 1, 5);
+Error, must be a vertex of ,
+
+# Minimum cut set: source = dest
+gap> d := EdgeWeightedDigraph([[2], []], [[5], []]);
+
+gap> DigraphMinimumCutSet(d, 1, 1);
+Error, and must be distinct
+
#############################################################################
# 6. Random edge-weighted digraphs
#############################################################################