Skip to content

Commit 8af98e6

Browse files
Added DigraphMinimumCutSet (#879)
* Added DigraphMinimumCut * Some nonsense happening in prop * renamed DigraphMinimumCut to DigraphMinimumCutSet * lint * Improved function and updated doc * minor optimisations * fixed tests * fix typo * fix typo in doc * Added additional tests * Very minor nit in docs. --------- Co-authored-by: reiniscirpons <rc234@st-andrews.ac.uk>
1 parent ff2ff7c commit 8af98e6

File tree

5 files changed

+252
-3
lines changed

5 files changed

+252
-3
lines changed

doc/weights.xml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,64 @@ gap> Sum(flow[1]);
272272
</ManSection>
273273
<#/GAPDoc>
274274

275+
<#GAPDoc Label="DigraphMinimumCut">
276+
<ManSection>
277+
<Attr Name="DigraphMinimumCut" Arg="digraph, s, t"/>
278+
<Returns>A list of lists of integers.</Returns>
279+
<Description>
280+
If <A>digraph</A> is an edge-weighted digraph with distinct vertices <A>s</A>
281+
and <A>t</A>, this function returns a list of two lists representing the
282+
components of the minimum <M>s</M>-<M>t</M> cut of <A>digraph</A>. <P/>
283+
284+
An <E><M>s</M>-<M>t</M> cut</E> is a partition of the vertices <M>\{ S, T \}</M>
285+
such that <A>s</A> is in <M>S</M> and <A>t</A> is in <M>T</M>. The <E>capacity</E>
286+
of an <M>s</M>-<M>t</M> cut is the sum of the weights of every edge whose source
287+
is in <M>S</M> and whose range is in <M>T</M>. The minimum <M>s</M>-<M>t</M> cut is
288+
the <M>s</M>-<M>t</M> cut whose capacity is the smallest possible.<P/>
289+
290+
This attribute is computed by using <Ref Func="DigraphMaximumFlow"/> and the
291+
max-cut min-flow theorem.<P/>
292+
293+
See <Ref Attr="EdgeWeights" Func="EdgeWeightedDigraph"/>.
294+
<Example><![CDATA[
295+
gap> g := EdgeWeightedDigraph([[2, 2], [3], []], [[3, 2], [1], []]);
296+
<immutable edge-weighted multidigraph with 3 vertices, 3 edges>
297+
gap> DigraphMinimumCut(g, 1, 3);
298+
[ [ 1, 2 ], [3] ]
299+
]]></Example>
300+
</Description>
301+
</ManSection>
302+
<#/GAPDoc>
303+
304+
<#GAPDoc Label="DigraphMinimumCutSet">
305+
<ManSection>
306+
<Attr Name="DigraphMinimumCutSet" Arg="digraph, s, t"/>
307+
<Returns>A list of lists integers.</Returns>
308+
<Description>
309+
If <A>digraph</A> is an edge-weighted digraph with distinct vertices <A>s</A>
310+
and <A>t</A>, this function returns a list of lists of integers representing the
311+
minimum <M>s</M>-<M>t</M> cut of <A>digraph</A>. <P/>
312+
313+
An <E><M>s</M>-<M>t</M> cut</E> is a partition of the vertices <M>\{ S, T \}</M>
314+
such that <A>s</A> is in <M>S</M> and <A>t</A> is in <M>T</M>. The <E>cut set</E>
315+
corresponding to this cut is the set of edges whose source is in <M>S</M> and
316+
whose range is in <M>T</M>. The minimum <M>s</M>-<M>t</M> cut set is
317+
the <M>s</M>-<M>t</M> cut set such that the sum of the weights of the edges in
318+
the cut set is the smallest possible.<P/>
319+
320+
This attribute is computed by using <Ref Func="DigraphMinimumCut"/>.<P/>
321+
322+
See <Ref Attr="EdgeWeights" Func="EdgeWeightedDigraph"/>.
323+
<Example><![CDATA[
324+
gap> g := EdgeWeightedDigraph([[2, 2], [3], []], [[3, 2], [1], []]);
325+
<immutable edge-weighted multidigraph with 3 vertices, 3 edges>
326+
gap> DigraphMinimumCutSet(g, 1, 3);
327+
[ [ 2, 3 ] ]
328+
]]></Example>
329+
</Description>
330+
</ManSection>
331+
<#/GAPDoc>
332+
275333
<#GAPDoc Label="RandomUniqueEdgeWeightedDigraph">
276334
<ManSection>
277335
<Oper Name="RandomUniqueEdgeWeightedDigraph" Arg="[filt, ]n[, p]"/>

doc/z-chap5.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
<#Include Label="EdgeWeightedDigraphShortestPaths">
3333
<#Include Label="EdgeWeightedDigraphShortestPath">
3434
<#Include Label="DigraphMaximumFlow">
35+
<#Include Label="DigraphMinimumCut">
36+
<#Include Label="DigraphMinimumCutSet">
3537
<#Include Label="RandomUniqueEdgeWeightedDigraph">
3638
</Section>
3739

gap/weights.gd

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,13 @@ DeclareGlobalFunction("DIGRAPHS_Edge_Weighted_FloydWarshall");
3535
DeclareGlobalFunction("DIGRAPHS_Edge_Weighted_Bellman_Ford");
3636
DeclareGlobalFunction("DIGRAPHS_Edge_Weighted_Dijkstra");
3737

38-
# 5. Maximum Flow
38+
# 5. Maximum Flow and Minimum Cut
3939
DeclareOperation("DigraphMaximumFlow",
4040
[IsDigraph and HasEdgeWeights, IsPosInt, IsPosInt]);
41+
DeclareOperation("DigraphMinimumCut",
42+
[IsDigraph and HasEdgeWeights, IsPosInt, IsPosInt]);
43+
DeclareOperation("DigraphMinimumCutSet",
44+
[IsDigraph and HasEdgeWeights, IsPosInt, IsPosInt]);
4145

4246
# 6. Random edge weighted digraphs
4347
DeclareOperation("RandomUniqueEdgeWeightedDigraph", [IsPosInt]);

gap/weights.gi

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,7 @@ function(D, source)
638638
end);
639639

640640
#############################################################################
641-
# 5. Maximum Flow
641+
# 5. Maximum Flow and Minimum Cut
642642
#############################################################################
643643

644644
InstallMethod(DigraphMaximumFlow, "for an edge weighted digraph",
@@ -772,6 +772,87 @@ function(D, start, destination)
772772
return flows;
773773
end);
774774

775+
InstallMethod(DigraphMinimumCut, "for an edge weighted digraph",
776+
[IsDigraph and HasEdgeWeights, IsPosInt, IsPosInt],
777+
function(D, s, t)
778+
local weights, outs, vertices, flow, residuals, u, v, S, T, seen, i;
779+
780+
# Extract important data
781+
weights := EdgeWeights(D);
782+
outs := OutNeighbours(D);
783+
vertices := DigraphVertices(D);
784+
785+
# Check input
786+
if s < 1 or s > Length(vertices) then
787+
ErrorNoReturn("<s> must be a vertex of <D>,");
788+
elif t < 1 or t > Length(vertices) then
789+
ErrorNoReturn("<t> must be a vertex of <D>,");
790+
elif s = t then
791+
ErrorNoReturn("<s> and <t> must be distinct");
792+
fi;
793+
794+
# Find the residual edge capacities under the maximum flow
795+
flow := DigraphMaximumFlow(D, s, t);
796+
residuals := weights - flow;
797+
798+
# Carry out a BFS to find all the vertices in the residual
799+
# network which are reachable from s. This gives the minimum
800+
# cut by the max-flow min-cut theorem.
801+
802+
S := [s];
803+
seen := BlistList(DigraphVertices(D), [s]);
804+
i := 1;
805+
while i <= Length(S) do
806+
u := S[i];
807+
i := i + 1;
808+
for v in [1 .. Length(outs[u])] do
809+
if residuals[u][v] > 0 then
810+
if not seen[outs[u][v]] then
811+
Add(S, outs[u][v]);
812+
seen[outs[u][v]] := true;
813+
fi;
814+
fi;
815+
od;
816+
od;
817+
818+
T := Difference(vertices, S);
819+
return [S, T];
820+
end);
821+
822+
InstallMethod(DigraphMinimumCutSet, "for an edge weighted digraph",
823+
[IsDigraph and HasEdgeWeights, IsPosInt, IsPosInt],
824+
function(D, s, t)
825+
local cut, cutset, u, v, S, T;
826+
827+
# Check input
828+
if s < 1 or s > DigraphNrVertices(D) then
829+
ErrorNoReturn("<s> must be a vertex of <D>,");
830+
elif t < 1 or t > DigraphNrVertices(D) then
831+
ErrorNoReturn("<t> must be a vertex of <D>,");
832+
elif s = t then
833+
ErrorNoReturn("<s> and <t> must be distinct");
834+
fi;
835+
836+
# Get the minimum cut
837+
838+
cut := DigraphMinimumCut(D, s, t);
839+
S := cut[1];
840+
T := cut[2];
841+
842+
# Compute the cut-set corresponding to the minimum cut
843+
844+
cutset := [];
845+
for u in S do
846+
for v in OutNeighbours(D)[u] do
847+
if v in T then
848+
Add(cutset, [u, v]);
849+
fi;
850+
od;
851+
od;
852+
853+
return cutset;
854+
end);
855+
775856
#############################################################################
776857
# 6. Random edge weighted digraphs
777858
#############################################################################

tst/standard/weights.tst

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ gap> EdgeWeightedDigraphShortestPath(d, 1, 3);
291291
[ [ 1, 2, 3 ], [ 1, 1 ] ]
292292
293293
#############################################################################
294-
# 5. Maximum Flow
294+
# 5. Maximum Flow and Minimum Cut
295295
#############################################################################
296296
297297
# Maximum flow: empty digraphs
@@ -368,6 +368,110 @@ gap> gr := EdgeWeightedDigraph([[2], [3, 6], [4], [1, 6], [1, 3], []],
368368
gap> DigraphMaximumFlow(gr, 5, 6);
369369
[ [ 10 ], [ 3, 7 ], [ 7 ], [ 0, 7 ], [ 10, 4 ], [ ] ]
370370
371+
# Minimum cut: empty digraphs
372+
gap> d := EdgeWeightedDigraph([], []);
373+
<immutable empty edge-weighted digraph with 0 vertices>
374+
gap> DigraphMinimumCut(d, 1, 1);
375+
Error, <s> must be a vertex of <D>,
376+
377+
# Minimum cut: single vertex (also empty digraphs)
378+
gap> d := EdgeWeightedDigraph([[]], [[]]);
379+
<immutable empty edge-weighted digraph with 1 vertex>
380+
gap> DigraphMinimumCut(d, 1, 1);
381+
Error, <s> and <t> must be distinct
382+
383+
# Minimum cut: source = dest
384+
gap> d := EdgeWeightedDigraph([[2], []], [[5], []]);
385+
<immutable edge-weighted digraph with 2 vertices, 1 edge>
386+
gap> DigraphMinimumCut(d, 1, 1);
387+
Error, <s> and <t> must be distinct
388+
389+
# Minimum cut: has loop
390+
gap> d := EdgeWeightedDigraph([[1, 2], []], [[5, 10], []]);
391+
<immutable edge-weighted digraph with 2 vertices, 2 edges>
392+
gap> DigraphMinimumCut(d, 1, 2);
393+
[ [ 1 ], [ 2 ] ]
394+
395+
# Minimum cut: invalid source
396+
gap> d := EdgeWeightedDigraph([[1, 2], []], [[5, 10], []]);
397+
<immutable edge-weighted digraph with 2 vertices, 2 edges>
398+
gap> DigraphMinimumCut(d, 5, 2);
399+
Error, <s> must be a vertex of <D>,
400+
401+
# Minimum cut: invalid sink
402+
gap> d := EdgeWeightedDigraph([[1, 2], []], [[5, 10], []]);
403+
<immutable edge-weighted digraph with 2 vertices, 2 edges>
404+
gap> DigraphMinimumCut(d, 1, 5);
405+
Error, <t> must be a vertex of <D>,
406+
407+
# Minimum cut: sink not reachable
408+
gap> d := EdgeWeightedDigraph([[1], []], [[5], []]);
409+
<immutable edge-weighted digraph with 2 vertices, 1 edge>
410+
gap> DigraphMinimumCut(d, 1, 2);
411+
[ [ 1 ], [ 2 ] ]
412+
413+
# Minimum cut: source has in neighbours
414+
gap> d := EdgeWeightedDigraph([[2], [3], []], [[5], [10], []]);
415+
<immutable edge-weighted digraph with 3 vertices, 2 edges>
416+
gap> DigraphMinimumCut(d, 2, 3);
417+
[ [ 2 ], [ 1, 3 ] ]
418+
419+
# Minimum cut: sink has out-neighbours
420+
gap> d := EdgeWeightedDigraph([[2], [3], [2]], [[5], [10], [7]]);
421+
<immutable edge-weighted digraph with 3 vertices, 3 edges>
422+
gap> DigraphMinimumCut(d, 2, 3);
423+
[ [ 2 ], [ 1, 3 ] ]
424+
425+
# Minimum cut: cycle
426+
gap> d := EdgeWeightedDigraph([[2], [3], [1]], [[5], [10], [7]]);
427+
<immutable edge-weighted digraph with 3 vertices, 3 edges>
428+
gap> DigraphMinimumCut(d, 1, 3);
429+
[ [ 1 ], [ 2, 3 ] ]
430+
431+
# Minimum cut: example from Wikipedia
432+
gap> gr := EdgeWeightedDigraph([[3, 4], [], [2, 4], [2]],
433+
> [[10, 5], [], [5, 15], [10]]);;
434+
gap> DigraphMinimumCut(gr, 1, 2);
435+
[ [ 1 ], [ 2, 3, 4 ] ]
436+
gap> DigraphMinimumCut(gr, 3, 2);
437+
[ [ 3, 4 ], [ 1, 2 ] ]
438+
439+
# Minimum cut: example from Wikipedia article on Push-label maximum flow
440+
gap> gr := EdgeWeightedDigraph([[2], [3, 6], [4], [1, 6], [1, 3], []],
441+
> [[12], [3, 7], [10], [5, 10], [15, 4], []]);;
442+
gap> DigraphMinimumCut(gr, 5, 6);
443+
[ [ 5, 1, 2 ], [ 3, 4, 6 ] ]
444+
445+
# Minimum cut set: empty digraphs
446+
gap> d := EdgeWeightedDigraph([], []);
447+
<immutable empty edge-weighted digraph with 0 vertices>
448+
gap> DigraphMinimumCutSet(d, 1, 1);
449+
Error, <s> must be a vertex of <D>,
450+
451+
# Minimum cut set: source has in neighbours
452+
gap> d := EdgeWeightedDigraph([[2], [3], []], [[5], [10], []]);
453+
<immutable edge-weighted digraph with 3 vertices, 2 edges>
454+
gap> DigraphMinimumCutSet(d, 2, 3);
455+
[ [ 2, 3 ] ]
456+
457+
# Minimum cut set: cycle
458+
gap> d := EdgeWeightedDigraph([[2], [3], [1]], [[5], [10], [7]]);
459+
<immutable edge-weighted digraph with 3 vertices, 3 edges>
460+
gap> DigraphMinimumCutSet(d, 1, 3);
461+
[ [ 1, 2 ] ]
462+
463+
# Minimum cut set: invalid sink
464+
gap> d := EdgeWeightedDigraph([[1, 2], []], [[5, 10], []]);
465+
<immutable edge-weighted digraph with 2 vertices, 2 edges>
466+
gap> DigraphMinimumCutSet(d, 1, 5);
467+
Error, <t> must be a vertex of <D>,
468+
469+
# Minimum cut set: source = dest
470+
gap> d := EdgeWeightedDigraph([[2], []], [[5], []]);
471+
<immutable edge-weighted digraph with 2 vertices, 1 edge>
472+
gap> DigraphMinimumCutSet(d, 1, 1);
473+
Error, <s> and <t> must be distinct
474+
371475
#############################################################################
372476
# 6. Random edge-weighted digraphs
373477
#############################################################################

0 commit comments

Comments
 (0)