diff --git a/doc/oper.xml b/doc/oper.xml index 0930e8f3c..4f7cbd8ae 100644 --- a/doc/oper.xml +++ b/doc/oper.xml @@ -1954,6 +1954,27 @@ rec( idom := [ 2, fail, 2, 2, 2 ], preorder := [ 2, 1, 3, 4, 5 ] ) <#/GAPDoc> +<#GAPDoc Label="DigraphDominatingSet"> + + + A List + + This function returns a dominating set of digraph, + which is a subset of vertices whose neighbourhood consists of all vertices in digraph. +

+ + The neighbourhood of a set of vertices, V, refers to the set of vertices that are adjacent to a vertex in V, + not including any vertices that exist in V. The domating set returned will be one of potentially multiple possible dominating sets for the digraph. + DigraphDominatingSet(D); +[ 4, 1 ] +gap> DigraphDominatingSet(D); +[ 1 ]]]> + + +<#/GAPDoc> + <#GAPDoc Label="PartialOrderDigraphMeetOfVertices"> Sum(flow[1]); <#/GAPDoc> +<#GAPDoc Label="DigraphEdgeConnectivity"> + + + An integer + + This function returns the edge connectivity of digraph.

Edge Connectivity refers to the size of a minimum cut of the graph, + which, for digraphs, refers to set of edges needed to be removed to ensure the graph is no longer Strongly Connected. That is, there exists two + vertices, A and B, for which a path from A to B does not exist. + + It makes use of the DigraphMaximumFlow(digraph) function, by constructing an edge-weighted Digraph + with edge weights of 1, then using the Max-flow min-cut theorem to determine the size of the minimum cut.

+ See also . + D := Digraph([[2, 3, 4], [1, 3, 4], [1, 2], [2, 3]]);; +gap> DigraphEdgeConnectivity(D); +2 +gap> D := Digraph([[], [1, 2], [2]]);; +gap> DigraphEdgeConnectivity(D); +0 +gap> D := Digraph([[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]);; +gap> DigraphEdgeConnectivity(D); +4]]> + + +<#/GAPDoc> + <#GAPDoc Label="RandomUniqueEdgeWeightedDigraph"> diff --git a/doc/z-chap4.xml b/doc/z-chap4.xml index 059e10eea..b2f0c32b7 100644 --- a/doc/z-chap4.xml +++ b/doc/z-chap4.xml @@ -76,6 +76,7 @@ <#Include Label="DigraphAbsorptionExpectedSteps"> <#Include Label="Dominators"> <#Include Label="DominatorTree"> + <#Include Label="DigraphDominatingSet"> <#Include Label="IteratorOfPaths"> <#Include Label="DigraphAllSimpleCircuits"> <#Include Label="DigraphLongestSimpleCircuit"> diff --git a/doc/z-chap5.xml b/doc/z-chap5.xml index 759876e11..a1be116c9 100644 --- a/doc/z-chap5.xml +++ b/doc/z-chap5.xml @@ -33,6 +33,7 @@ <#Include Label="EdgeWeightedDigraphShortestPath"> <#Include Label="DigraphMaximumFlow"> <#Include Label="RandomUniqueEdgeWeightedDigraph"> + <#Include Label="DigraphEdgeConnectivity">

Orders diff --git a/gap/oper.gd b/gap/oper.gd index f6a37e0f8..031efe812 100644 --- a/gap/oper.gd +++ b/gap/oper.gd @@ -154,6 +154,7 @@ DeclareOperation("IsOrderIdeal", [IsDigraph, IsList]); DeclareOperation("IsOrderFilter", [IsDigraph, IsList]); DeclareOperation("Dominators", [IsDigraph, IsPosInt]); DeclareOperation("DominatorTree", [IsDigraph, IsPosInt]); +DeclareOperation("DigraphDominatingSet", [IsDigraph]); DeclareOperation("DigraphCycleBasis", [IsDigraph]); # 10. Operations for vertices . . . diff --git a/gap/oper.gi b/gap/oper.gi index 3876db50e..452976985 100644 --- a/gap/oper.gi +++ b/gap/oper.gi @@ -2522,6 +2522,34 @@ function(D, root) return result; end); +# For calculating a dominating set for a digraph +# Algorithm 7 in : +# https://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf +InstallMethod(DigraphDominatingSet, "for a digraph", +[IsDigraph], +function(digraph) + local D, seen, Vertices, neighbour, v; + + Vertices := [1 .. DigraphNrVertices(digraph)]; + + # Shuffling not technically necessary - may be better not to? + Shuffle(Vertices); + + seen := BlistList([1 .. DigraphNrVertices(digraph)], []); + D := []; + for v in Vertices do + if seen[v] = false then + seen[v] := true; + Append(D, [v]); + for neighbour in OutNeighbours(digraph)[v] do + seen[neighbour] := true; + od; + fi; + od; + + return D; +end); + # Computes the fundamental cycle basis of a symmetric digraph # First, notice that the cycle space is composed of orthogonal subspaces # corresponding to the cycle spaces of the connected components. diff --git a/gap/weights.gd b/gap/weights.gd index 7d11bf5f7..c266616a2 100644 --- a/gap/weights.gd +++ b/gap/weights.gd @@ -39,6 +39,9 @@ DeclareGlobalFunction("DIGRAPHS_Edge_Weighted_Dijkstra"); DeclareOperation("DigraphMaximumFlow", [IsDigraph and HasEdgeWeights, IsPosInt, IsPosInt]); +# Digraph Edge Connectivity +DeclareOperation("DigraphEdgeConnectivity", [IsDigraph]); + # 6. Random edge weighted digraphs DeclareOperation("RandomUniqueEdgeWeightedDigraph", [IsPosInt]); DeclareOperation("RandomUniqueEdgeWeightedDigraph", [IsPosInt, IsFloat]); diff --git a/gap/weights.gi b/gap/weights.gi index 5b246b002..2595d87e6 100644 --- a/gap/weights.gi +++ b/gap/weights.gi @@ -772,6 +772,92 @@ function(D, start, destination) return flows; end); +############################################################################# +# Digraph Edge Connectivity +############################################################################# + +# Algorithms constructed off the algorithms detailed in: +# https://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf +# Each Algorithm uses a different method to decrease the time complexity, +# of calculating Edge Connectivity, though all make use of DigraphMaximumFlow() +# due to the Max-Flow, Min-Cut Theorem + +# Algorithm 1: Calculating the Maximum Flow of every possible source and sink +# Algorithm 2: Calculating the Maximum Flow to all sinks of an arbitrary source +# Algorithm 3: Finding Maximum Flow within the non-leaves of a Spanning Tree +# Algorithm 4: Constructing a spanning tree with a high number of leaves +# Algorithm 5: Using the spanning tree^ to find Maximum Flow within non-leaves +# Algorithm 6: Finding Maximum Flow within a dominating set of the digraph +# Algorithm 7: Constructing a dominating set for use in Algorithm 6 + +# Algorithms 4-7 are used below: + +# Digraph EdgeConnectivity calculated with Dominating Sets (Algorithm 6-7) +InstallMethod(DigraphEdgeConnectivity, "for a digraph", +[IsDigraph], +function(digraph) + # Form an identical but edge weighted digraph with all edge weights as 1: + local weights, i, u, v, w, neighbourhood, EdgeD, + maxFlow, min, sum, a, b, V, added, st, non_leaf, max, + notAddedNeighbours, notadded, NextVertex, NeighboursV, + neighbour, Edges, D, VerticesLeft, VerticesED; + + if DigraphNrVertices(digraph) = 1 or + DigraphNrStronglyConnectedComponents(digraph) > 1 then + return 0; + fi; + + weights := List([1 .. DigraphNrVertices(digraph)], + x -> List([1 .. Length(OutNeighbours(digraph)[x])], + y -> 1)); + EdgeD := EdgeWeightedDigraph(digraph, weights); + + min := -1; + + # Algorithm 7: Creating a dominating set of the digraph + D := DigraphDominatingSet(digraph); + + # Algorithm 6: Using the dominating set created to determine the Maximum Flow + + if Length(D) > 1 then + + v := D[1]; + for i in [2 .. Length(D)] do + w := D[i]; + a := DigraphMaximumFlow(EdgeD, v, w)[v]; + b := DigraphMaximumFlow(EdgeD, w, v)[w]; + + sum := Minimum(Sum(a), Sum(b)); + if (sum < min or min = -1) then + min := sum; + fi; + od; + + else + # If the dominating set of EdgeD is of Length 1, + # the above algorithm will not work + # Revert to iterating through all vertices of the original digraph + + u := 1; + + for v in [2 .. DigraphNrVertices(EdgeD)] do + a := DigraphMaximumFlow(EdgeD, u, v)[u]; + b := DigraphMaximumFlow(EdgeD, v, u)[v]; + + sum := Minimum(Sum(a), Sum(b)); + if (sum < min or min = -1) then + min := sum; + fi; + + od; + fi; + + return Minimum(min, + Minimum(Minimum(OutDegrees(EdgeD)), + Minimum(InDegrees(EdgeD)))); + +end); + ############################################################################# # 6. Random edge weighted digraphs ############################################################################# diff --git a/tst/standard/oper.tst b/tst/standard/oper.tst index 1c813fe21..6d45717c6 100644 --- a/tst/standard/oper.tst +++ b/tst/standard/oper.tst @@ -134,6 +134,18 @@ gap> M := DigraphMutableCopy(D);; gap> M ^ p = OnDigraphs(M, p); true +# DigraphRemoveAllEdges: for a digraph +gap> gr2 := Digraph(IsMutableDigraph, [[2, 3], [3], [4], []]); + +gap> DigraphRemoveAllEdges(gr2); + +gap> gr3 := Digraph(IsMutableDigraph, [[], [], [], []]); + +gap> DigraphRemoveAllEdges(gr3); + +gap> OutNeighbours(gr3); +[ [ ], [ ], [ ], [ ] ] + # OnDigraphs: for a digraph and a perm gap> gr := Digraph([[2], [1], [3]]); @@ -3324,6 +3336,40 @@ gap> DigraphEdges(D); gap> DigraphVertexLabels(D); [ 1, 2, 3, 6, [ 4, 5 ] ] +# DigraphDominatingSet +gap> d := Digraph([[2, 3], [2, 3], [1, 2, 3]]);; +gap> p := DigraphDominatingSet(d);; +gap> neighbours := [];; +gap> for v in p do +> Append(neighbours, OutNeighbours(d)[v]); +> od; +gap> DigraphVertices(d) = Union(neighbours, p); +true +gap> d := Digraph([[2, 4], [3], [1, 5], [3], [4]]);; +gap> p := DigraphDominatingSet(d);; +gap> neighbours := [];; +gap> for v in p do +> Append(neighbours, OutNeighbours(d)[v]); +> od; +gap> DigraphVertices(d) = Union(neighbours, p); +true +gap> D := Digraph([[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]);; +gap> p := DigraphDominatingSet(d);; +gap> neighbours := [];; +gap> for v in p do +> Append(neighbours, OutNeighbours(d)[v]); +> od; +gap> DigraphVertices(d) = Union(neighbours, p); +true +gap> D := RandomDigraph(1);; +gap> p := DigraphDominatingSet(d);; +gap> neighbours := [];; +gap> for v in p do +> Append(neighbours, OutNeighbours(d)[v]); +> od; +gap> DigraphVertices(d) = Union(neighbours, p); +true + # gap> DIGRAPHS_StopTest(); gap> STOP_TEST("Digraphs package: standard/oper.tst", 0); diff --git a/tst/standard/weights.tst b/tst/standard/weights.tst index 59a632140..23d28a0a3 100644 --- a/tst/standard/weights.tst +++ b/tst/standard/weights.tst @@ -368,6 +368,23 @@ gap> gr := EdgeWeightedDigraph([[2], [3, 6], [4], [1, 6], [1, 3], []], gap> DigraphMaximumFlow(gr, 5, 6); [ [ 10 ], [ 3, 7 ], [ 7 ], [ 0, 7 ], [ 10, 4 ], [ ] ] +# EdgeConnectivity +gap> d := Digraph([[2, 3], [2, 3], [1, 2, 3]]);; +gap> DigraphEdgeConnectivity(d); +1 +gap> D := RandomDigraph(1);; +gap> DigraphEdgeConnectivity(D); +0 +gap> d := Digraph([[2, 4], [3], [1, 5], [3], [4]]);; +gap> DigraphEdgeConnectivity(d); +1 +gap> D := Digraph([[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]);; +gap> DigraphEdgeConnectivity(D); +4 +gap> D := DigraphFromGraph6String("I~~~~~~~w");; +gap> DigraphEdgeConnectivity(D); +9 + ############################################################################# # 6. Random edge-weighted digraphs ############################################################################# diff --git a/tst/testinstall.tst b/tst/testinstall.tst index 291b09b7a..02224e66c 100644 --- a/tst/testinstall.tst +++ b/tst/testinstall.tst @@ -550,6 +550,20 @@ gap> OutNeighbours(D); gap> OutNeighbours(C); [ [ 2, 3, 4 ], [ 1, 3, 4, 5 ], [ 1, 2 ], [ 5 ], [ 4 ] ] +# DigraphEdgeConnectivity +gap> D := Digraph([[2, 3, 4], [1, 3, 4], [1, 2], [2, 3]]);; +gap> DigraphEdgeConnectivity(D); +2 +gap> D := Digraph([[], [1, 2], [2]]);; +gap> DigraphEdgeConnectivity(D); +0 +gap> C := Digraph([[3, 4], [1, 3, 4], [2], [3]]);; +gap> DigraphEdgeConnectivity(C); +1 +gap> D := Digraph([[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]);; +gap> DigraphEdgeConnectivity(D); +4 + # gap> DIGRAPHS_StopTest(); gap> STOP_TEST("Digraphs package: testinstall.tst", 0);