diff --git a/doc/oper.xml b/doc/oper.xml index 0930e8f3c..8a8940e5b 100644 --- a/doc/oper.xml +++ b/doc/oper.xml @@ -1954,6 +1954,46 @@ rec( idom := [ 2, fail, 2, 2, 2 ], preorder := [ 2, 1, 3, 4, 5 ] ) <#/GAPDoc> +<#GAPDoc Label="DigraphDominatingSet"> + + + A List + + This creates a dominating set of digraph, which is a subset of the vertices in + digraph such that the neighbourhood of this subset of vertices is equal to all + the vertices in digraph.

+ + 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="DigraphGetNeighbourhood"> + + + A List + + This returns the set of vertices that are adjacent to a vertex in vertices, + not including any vertices that exist in vertices. + D := Digraph([[2, 3, 4], [1], [], []]);; +gap> DigraphGetNeighbourhood(D, [1]); +[ 2, 3, 4 ] +gap> DigraphGetNeighbourhood(D, [1, 2]); +[ 3, 4 ] +gap> D := Digraph([[2, 3, 4], [], [], []]);; +gap> DigraphGetNeighbourhood(D, [2]); +[ ]]]> + + +<#/GAPDoc> + <#GAPDoc Label="PartialOrderDigraphMeetOfVertices"> Sum(flow[1]); <#/GAPDoc> +<#GAPDoc Label="DigraphEdgeConnectivity"> + + + An integer + + This returns a record representing the edge connectivity of digraph.

+ + It makes use of DigraphMaximumFlow(digraph), 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. + For a digraph, the minimum cut is the set of edges that need to be removed to ensure the digraph is + no longer Strongly Connected.

+ 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/gap/oper.gd b/gap/oper.gd index f6a37e0f8..f0cb7c3f0 100644 --- a/gap/oper.gd +++ b/gap/oper.gd @@ -154,6 +154,8 @@ DeclareOperation("IsOrderIdeal", [IsDigraph, IsList]); DeclareOperation("IsOrderFilter", [IsDigraph, IsList]); DeclareOperation("Dominators", [IsDigraph, IsPosInt]); DeclareOperation("DominatorTree", [IsDigraph, IsPosInt]); +DeclareOperation("DigraphDominatingSet", [IsDigraph]); +DeclareOperation("DigraphGetNeighbourhood", [IsDigraph, IsList]); DeclareOperation("DigraphCycleBasis", [IsDigraph]); # 10. Operations for vertices . . . diff --git a/gap/oper.gi b/gap/oper.gi index 3876db50e..095c59102 100644 --- a/gap/oper.gi +++ b/gap/oper.gi @@ -2522,6 +2522,45 @@ 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, neighbourhood, VerticesLeft, Vertices; + + Vertices := [1 .. DigraphNrVertices(digraph)]; + Shuffle(Vertices); + + # Shuffling not technically necessary - may be better to just do D := [1]? + D := [Vertices[1]]; + neighbourhood := DigraphGetNeighbourhood(digraph, D); + VerticesLeft := Difference(Vertices, Union(neighbourhood, D)); + + while not IsEmpty(VerticesLeft) do; + Append(D, [VerticesLeft[1]]); + neighbourhood := DigraphGetNeighbourhood(digraph, D); + VerticesLeft := Difference(Vertices, Union(neighbourhood, D)); + od; + + return D; +end); + +# For getting the neighbourhood for a given List of Vertices in a Digraph +InstallMethod(DigraphGetNeighbourhood, "for a digraph and a List of vertices", +[IsDigraph, IsList], +function(digraph, vertices) + local v, neighbourhood; + neighbourhood := []; + for v in vertices do + neighbourhood := Difference(Union(neighbourhood, + OutNeighbours(digraph)[v]), vertices); + od; + + return neighbourhood; +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..984ea67d3 100644 --- a/gap/weights.gd +++ b/gap/weights.gd @@ -39,6 +39,10 @@ DeclareGlobalFunction("DIGRAPHS_Edge_Weighted_Dijkstra"); DeclareOperation("DigraphMaximumFlow", [IsDigraph and HasEdgeWeights, IsPosInt, IsPosInt]); +# Digraph Edge Connectivity +DeclareOperation("DigraphEdgeConnectivity", [IsDigraph]); +DeclareOperation("DigraphEdgeConnectivityDS", [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..59a0241f0 100644 --- a/gap/weights.gi +++ b/gap/weights.gi @@ -772,6 +772,211 @@ 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: + +# DigraphEdgeConnectivity calculated using Spanning Trees (Algorithm 4 & 5) +InstallMethod(DigraphEdgeConnectivity, "for a digraph", +[IsDigraph], +function(digraph) + local w, weights, EdgeD, VerticesED, min, u, v, + sum, a, b, st, added, NeighboursV, Edges, notadded, + max, NextVertex, notAddedNeighbours, non_leaf; + + if DigraphNrVertices(digraph) = 1 then + return 0; + fi; + + if 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); + VerticesED := [1 .. DigraphNrVertices(EdgeD)]; + + min := -1; + + # Algorithm 4: Constructing a Spanning Tree with a large numbers of leaves + + st := EmptyDigraph(DigraphNrVertices(EdgeD)); + v := 1; + added := [v]; + notadded := Difference(VerticesED, added); + + while (DigraphNrEdges(st) < DigraphNrVertices(EdgeD) - 1) and + Length(notadded) > 0 do + + # Add all edges incident from v + NeighboursV := Difference(OutNeighbors(EdgeD)[v], added); + + Edges := List([1 .. Length(NeighboursV)], + x -> [v, NeighboursV[x]]); + + # Edges := Difference(Edges, [[v, v]]); + + st := DigraphAddEdges(st, Edges); + Append(added, Difference(OutNeighbours(EdgeD)[v], added)); + + # Select the neighbour to v with the highest number of not-added neighbours: + notadded := Difference(VerticesED, added); + max := 0; + NextVertex := v; + + # Preventing infinite iteration if v has no non-added neighbours + if (Length(NeighboursV) = 0) then + # Pick from a vertex that hasn't been added yet + NextVertex := notadded[1]; + fi; + + for w in NeighboursV do; + notAddedNeighbours := Intersection(notadded, OutNeighbours(EdgeD)[w]); + if (Length(notAddedNeighbours) > max) then + max := Length(notAddedNeighbours); + NextVertex := w; + fi; + od; + + v := NextVertex; + + od; + + # Algorithm 5: Iterating through the non-leaves of the + # Spanning Tree created in Algorithm 4 to find the Edge Connectivity + non_leaf := []; + for b in VerticesED do + if not IsEmpty(OutNeighbours(st)[b]) then + Append(non_leaf, [b]); + fi; + od; + + if (Length(non_leaf) > 1) then + u := non_leaf[1]; + + for v in [2 .. Length(non_leaf)] do + a := DigraphMaximumFlow(EdgeD, u, non_leaf[v])[u]; + b := DigraphMaximumFlow(EdgeD, non_leaf[v], u)[non_leaf[v]]; + + sum := Minimum(Sum(a), Sum(b)); + if (sum < min or min = -1) then + min := sum; + fi; + od; + + min := Minimum(min, Minimum(Minimum(OutDegrees(EdgeD)), + Minimum(InDegrees(EdgeD)))); + else + # In the case of spanning trees with only one non-leaf node, + # the above algorithm does 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; + + if (min = -1) then + return 0; + fi; + + return min; +end); + +# Digraph EdgeConnectivity calculated with Dominating Sets (Algorithm 6-7) +InstallMethod(DigraphEdgeConnectivityDS, "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 then + return 0; + fi; + + if 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..2bba479ca 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]]); @@ -2845,6 +2857,21 @@ rec( idom := [ fail ], preorder := [ 1 ] ) gap> DominatorTree(D, 6); rec( idom := [ ,,,,, fail ], preorder := [ 6 ] ) +# DigraphGetNeighbourhood +gap> D := Digraph([[2, 3, 4], [1], [], []]);; +gap> DigraphGetNeighbourhood(D, [1]); +[ 2, 3, 4 ] +gap> DigraphGetNeighbourhood(D, [1, 2]); +[ 3, 4 ] +gap> D := Digraph([[2, 3, 4], [], [], []]);; +gap> DigraphGetNeighbourhood(D, [2]); +[ ] +gap> D := Digraph([[2], [3], [4], [1]]);; +gap> DigraphGetNeighbourhood(D, [1]); +[ 2 ] +gap> DigraphGetNeighbourhood(D, [1, 3]); +[ 2, 4 ] + # IsDigraphPath gap> D := Digraph(IsMutableDigraph, Combinations([1 .. 5]), IsSubset); @@ -3324,6 +3351,24 @@ 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> DigraphVertices(d) = Union(DigraphGetNeighbourhood(d, p), p); +true +gap> d := Digraph([[2, 4], [3], [1, 5], [3], [4]]);; +gap> p := DigraphDominatingSet(d);; +gap> DigraphVertices(d) = Union(DigraphGetNeighbourhood(d, p), 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> DigraphVertices(d) = Union(DigraphGetNeighbourhood(d, p), p); +true +gap> D := RandomDigraph(1);; +gap> p := DigraphDominatingSet(d);; +gap> DigraphVertices(d) = Union(DigraphGetNeighbourhood(d, p), 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..d18a4e989 100644 --- a/tst/standard/weights.tst +++ b/tst/standard/weights.tst @@ -368,6 +368,40 @@ 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 + +# EdgeConnectivity (Dominating Set Algorithm) +gap> d := Digraph([[2, 3], [2, 3], [1, 2, 3]]);; +gap> DigraphEdgeConnectivityDS(d); +1 +gap> D := RandomDigraph(1);; +gap> DigraphEdgeConnectivityDS(D); +0 +gap> d := Digraph([[2, 4], [3], [1, 5], [3], [4]]);; +gap> DigraphEdgeConnectivityDS(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> DigraphEdgeConnectivityDS(D); +4 +gap> D := DigraphFromGraph6String("I~~~~~~~w");; +gap> DigraphEdgeConnectivityDS(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);