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);