diff --git a/doc/weights.xml b/doc/weights.xml index 22c6b71e9..b69369a12 100644 --- a/doc/weights.xml +++ b/doc/weights.xml @@ -272,6 +272,34 @@ 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 returns a list of two lists representing the components of + a minimal 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. A minimum s-t cut is an s-t + cut whose capacity is minimal.

+ + 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); +[ [ 2, 1 ], [3]] +]]> + + +<#/GAPDoc> + <#GAPDoc Label="RandomUniqueEdgeWeightedDigraph"> diff --git a/doc/z-chap5.xml b/doc/z-chap5.xml index 759876e11..64454c8d0 100644 --- a/doc/z-chap5.xml +++ b/doc/z-chap5.xml @@ -32,6 +32,7 @@ <#Include Label="EdgeWeightedDigraphShortestPaths"> <#Include Label="EdgeWeightedDigraphShortestPath"> <#Include Label="DigraphMaximumFlow"> + <#Include Label="DigraphMinimumCut"> <#Include Label="RandomUniqueEdgeWeightedDigraph"> diff --git a/gap/weights.gd b/gap/weights.gd index 7d11bf5f7..6930da8da 100644 --- a/gap/weights.gd +++ b/gap/weights.gd @@ -35,9 +35,11 @@ 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]); # 6. Random edge weighted digraphs DeclareOperation("RandomUniqueEdgeWeightedDigraph", [IsPosInt]); diff --git a/gap/weights.gi b/gap/weights.gi index 5b246b002..8c2838e9b 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,52 @@ 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, Q; + + # 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]; + Q := [s]; + while not IsEmpty(Q) do + u := Q[1]; + Remove(Q, 1); + for v in [1 .. Length(outs[u])] do + if residuals[u][v] > 0 then + if not outs[u][v] in S then + Add(Q, outs[u][v]); + Add(S, outs[u][v]); + fi; + fi; + od; + od; + + T := Difference(vertices, S); + return [S, T]; +end); + ############################################################################# # 6. Random edge weighted digraphs ############################################################################# diff --git a/tst/standard/weights.tst b/tst/standard/weights.tst index 59a632140..3e1a20bc1 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,80 @@ 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); +[ [ 4, 3 ], [ 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); +[ [ 1, 2, 5 ], [ 3, 4, 6 ] ] + ############################################################################# # 6. Random edge-weighted digraphs #############################################################################