Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions doc/oper.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1954,6 +1954,27 @@ rec( idom := [ 2, fail, 2, 2, 2 ], preorder := [ 2, 1, 3, 4, 5 ] )
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="DigraphDominatingSet">
<ManSection>
<Oper Name="DigraphDominatingSet" Arg="digraph"/>
<Returns>A List</Returns>
<Description>
This function returns a <E>dominating set</E> of <A>digraph</A>,
which is a subset of vertices whose neighbourhood consists of all vertices in <A>digraph</A>.
<P/>

The neighbourhood of a set of vertices, <A>V</A>, refers to the set of vertices that are adjacent to a vertex in <A>V</A>,
not including any vertices that exist in <A>V</A>. The domating set returned will be one of potentially multiple possible dominating sets for the digraph.
<Example><![CDATA[
D := Digraph([[2,3,4], [],[],[]]);;
gap> DigraphDominatingSet(D);
[ 4, 1 ]
gap> DigraphDominatingSet(D);
[ 1 ]]]></Example>
</Description>
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="PartialOrderDigraphMeetOfVertices">
<ManSection>
<Oper Name="PartialOrderDigraphMeetOfVertices"
Expand Down
26 changes: 26 additions & 0 deletions doc/weights.xml
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,32 @@ gap> Sum(flow[1]);
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="DigraphEdgeConnectivity">
<ManSection>
<Attr Name="DigraphEdgeConnectivity" Arg="digraph"/>
<Returns>An integer</Returns>
<Description>
This function returns the edge connectivity of <A>digraph</A>.<P/> 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>A</A> and <A>B</A>, for which a path from <A>A</A> to <A>B</A> does not exist.

It makes use of the <C>DigraphMaximumFlow(<A>digraph</A>)</C> 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.<P/>
See also <Ref Attr="DigraphMaximumFlow"/>.
<Example><![CDATA[
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> 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]]></Example>
</Description>
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="RandomUniqueEdgeWeightedDigraph">
<ManSection>
<Oper Name="RandomUniqueEdgeWeightedDigraph" Arg="[filt, ]n[, p]"/>
Expand Down
1 change: 1 addition & 0 deletions doc/z-chap4.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down
1 change: 1 addition & 0 deletions doc/z-chap5.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<#Include Label="EdgeWeightedDigraphShortestPath">
<#Include Label="DigraphMaximumFlow">
<#Include Label="RandomUniqueEdgeWeightedDigraph">
<#Include Label="DigraphEdgeConnectivity">
</Section>

<Section><Heading>Orders</Heading>
Expand Down
1 change: 1 addition & 0 deletions gap/oper.gd
Original file line number Diff line number Diff line change
Expand Up @@ -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 . . .
Expand Down
28 changes: 28 additions & 0 deletions gap/oper.gi
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the method work for all digraphs or only symmetric digraphs? If its only intended for symmetric digraphs, then this should be explicitly checked for in the function and mentioned in the documentation.

If its meant to work for all digraphs, it matters whether the dominating set is with respect to in-neighbours or out-neighbours, so the function name should be updated (I think the currently implemented function returns the out-dominating set), say DigraphOutDominatingSet, and a dual function for in-neighbours should be implemented.

Alternatively, the function could also take the symmetric closure of the digraph, this way it would be a dominating set with respect the notion of undirected adjacency (this might be called a weak dominating set for a directed graph? Not sure).

[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.
Expand Down
3 changes: 3 additions & 0 deletions gap/weights.gd
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down
86 changes: 86 additions & 0 deletions gap/weights.gi
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Comment on lines +836 to +853
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part of code is almost the same across the two EdgeConnectivity functions, so should be factored out into its own helper function. Alternatively, if the EdgeConnectivity or EdgeConnectivityDS functions get merged or one of them gets removed then this gets resolved too.


return Minimum(min,
Minimum(Minimum(OutDegrees(EdgeD)),
Minimum(InDegrees(EdgeD))));

end);

#############################################################################
# 6. Random edge weighted digraphs
#############################################################################
Expand Down
46 changes: 46 additions & 0 deletions tst/standard/oper.tst
Original file line number Diff line number Diff line change
Expand Up @@ -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], []]);
<mutable digraph with 4 vertices, 4 edges>
gap> DigraphRemoveAllEdges(gr2);
<mutable empty digraph with 4 vertices>
gap> gr3 := Digraph(IsMutableDigraph, [[], [], [], []]);
<mutable empty digraph with 4 vertices>
gap> DigraphRemoveAllEdges(gr3);
<mutable empty digraph with 4 vertices>
gap> OutNeighbours(gr3);
[ [ ], [ ], [ ], [ ] ]

# OnDigraphs: for a digraph and a perm
gap> gr := Digraph([[2], [1], [3]]);
<immutable digraph with 3 vertices, 3 edges>
Expand Down Expand Up @@ -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);
17 changes: 17 additions & 0 deletions tst/standard/weights.tst
Original file line number Diff line number Diff line change
Expand Up @@ -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
#############################################################################
Expand Down
14 changes: 14 additions & 0 deletions tst/testinstall.tst
Original file line number Diff line number Diff line change
Expand Up @@ -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);