Skip to content

Commit

Permalink
oper: add dominators algorithm
Browse files Browse the repository at this point in the history
Co-authored-by: Marina Anagnostopoulou-Merkouri <[email protected]>
Co-authored-by: Samantha Harper <[email protected]>
  • Loading branch information
3 people committed Mar 1, 2021
1 parent e097454 commit ffc9ed8
Show file tree
Hide file tree
Showing 7 changed files with 459 additions and 5 deletions.
20 changes: 20 additions & 0 deletions PackageInfo.g
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ SourceRepository := rec(
Persons := [

rec(
LastName := "Anagnostopoulou-Merkouri",
FirstNames := "Marina",
IsAuthor := false,
IsMaintainer := false,
Email := "[email protected]",
PostalAddress := _STANDREWSMATHS,
Place := "St Andrews",
Institution := "University of St Andrews"),

rec(
LastName := "De Beule",
FirstNames := "Jan",
IsAuthor := true,
Expand Down Expand Up @@ -89,6 +99,16 @@ Persons := [
Place := "St Andrews",
Institution := "University of St Andrews"),

rec(
LastName := "Harper",
FirstNames := "Samantha",
IsAuthor := false,
IsMaintainer := false,
Email := "[email protected]",
PostalAddress := _STANDREWSMATHS,
Place := "St Andrews",
Institution := "University of St Andrews"),

rec(
LastName := "Horn",
FirstNames := "Max",
Expand Down
13 changes: 13 additions & 0 deletions doc/digraphs.bib
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@ @inproceedings{JK07
Year = {2007},
}

@article{LT79,
Author = {Thomas Lengauer and Robert E. Tarjan},
Doi = {https://doi.org/10.1145/357062.357071},
Journal = {ACM Transactions on Programming Languages and Systems},
Number = {1},
Pages = {121--141},
Title = {A Fast Algorithm for Finding Dominators in a Flowgraph},
Url = {https://doi.org/10.1145/357062.357071},
Volume = {1},
Year = {1979},
Bdsk-Url-1 = {https://www.cs.princeton.edu/courses/archive/spr03/cs423/download/dominators.pdf}
}

@article{MP14,
Author = {Brendan D. McKay and Adolfo Piperno},
Doi = {https://dx.doi.org/10.1016/j.jsc.2013.09.003},
Expand Down
86 changes: 86 additions & 0 deletions doc/oper.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1668,6 +1668,92 @@ gap> DigraphShortestDistance(D, [1, 3], [3, 5]);
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="Dominators">
<ManSection>
<Oper Name="Dominators" Arg="digraph, root"/>
<Returns>A list of lists.</Returns>
<Description>
<C>Dominators</C> takes a <A>digraph</A> and a root <A>root</A> and returns
the dominators of each vertex with respect to the root. The output is
returned as a list of length <C>DigraphNrVertices(<A>Digraph</A>)</C>,
whose <A>i</A>th entry is a list with the dominators of vertex <A>i</A> of
the <A>digraph</A>. If there is no path from the root to a specific vertex,
the output will contain a hole in the corresponding position. The
<E>dominators</E> of a vertex <M>u</M> are the vertices that are
contained in every path from the <M>root</M> to <M>u</M>, not including
<M>u</M> itself.

The method for this operation is an implementation of an algorithm by
Thomas Lengauer and Robert Endre Tarjan <Cite Key="LT79"/>. The complexity
of this algorithm is <M>O(mlog n)</M> where <M>m</M> is the number of edges
and <M>n</M> is the number of nodes in the subdigraph induced by the nodes
in <A>digraph</A> reachable from <A>root</A>.
<Example><![CDATA[
gap> D := Digraph([[2], [3, 6], [2, 4], [1], [], [3]]);
<immutable digraph with 6 vertices, 7 edges>
gap> Dominators(D, 1);
[ , [ 1 ], [ 2, 1 ], [ 3, 2, 1 ],, [ 2, 1 ] ]
gap> Dominators(D, 2);
[ [ 4, 3, 2 ],, [ 2 ], [ 3, 2 ],, [ 2 ] ]
gap> Dominators(D, 3);
[ [ 4, 3 ], [ 3 ],, [ 3 ],, [ 2, 3 ] ]
gap> Dominators(D, 4);
[ [ 4 ], [ 1, 4 ], [ 2, 1, 4 ],,, [ 2, 1, 4 ] ]
gap> Dominators(D, 5);
[ ]
gap> Dominators(D, 6);
[ [ 4, 3, 6 ], [ 3, 6 ], [ 6 ], [ 3, 6 ] ]
]]></Example>
</Description>
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="DominatorTree">
<ManSection>
<Oper Name="DominatorTree" Arg="digraph, root"/>
<Returns>A record.</Returns>
<Description>
<C>DominatorTree</C> takes a <A>digraph</A> and a
<A>root</A> vertex and returns a record with the following components:
<List>
<Mark>idom</Mark>
<Item>
the immediate dominators of the vertices with respect
to the root.
</Item>
<Mark>preorder</Mark>
<Item>
the preorder values of the vertices defined by the depth first search
executed on the digraph.
</Item>
</List>
The <E>immediate dominator</E> of a vertex <M>u</M> is the unique dominator
of <M>u</M> that is dominated by all other dominators of <M>u</M>. The
algorithm is an implementation of the fast algorithm written by Thomas
Lengauer and Robert Endre Tarjan <Cite Key="LT79"/>. The complexity of
this algorithm is <M>O(mlog n)</M> where <M>m</M> is the number of edges
and <M>n</M> is the number of nodes in the subdigraph induced by the nodes
in <A>digraph</A> reachable from <A>root</A>.
<Example><![CDATA[
gap> D := Digraph([[2, 3], [4, 6], [4, 5], [3, 5], [1, 6], [2, 3]]);
<immutable digraph with 6 vertices, 12 edges>
gap> DominatorTree(D, 1);
rec( idom := [ fail, 1, 1, 1, 1, 1 ],
preorder := [ 1, 2, 4, 3, 5, 6 ] )
gap> DominatorTree(D, 5);
rec( idom := [ 5, 5, 5, 5, fail, 5 ],
preorder := [ 5, 1, 2, 4, 3, 6 ] )
gap> D := CompleteDigraph(5);
<immutable complete digraph with 5 vertices>
gap> DominatorTree(D, 1);
rec( idom := [ fail, 1, 1, 1, 1 ], preorder := [ 1, 2, 3, 4, 5 ] )
gap> DominatorTree(D, 2);
rec( idom := [ 2, fail, 2, 2, 2 ], preorder := [ 2, 1, 3, 4, 5 ] )
]]></Example>
</Description>
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="PartialOrderDigraphMeetOfVertices">
<ManSection>
<Oper Name="PartialOrderDigraphMeetOfVertices"
Expand Down
2 changes: 2 additions & 0 deletions doc/z-chap4.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
<#Include Label="VerticesReachableFrom">
<#Include Label="DigraphPath">
<#Include Label="DigraphShortestPath">
<#Include Label="Dominators">
<#Include Label="DominatorTree">
<#Include Label="IteratorOfPaths">
<#Include Label="DigraphAllSimpleCircuits">
<#Include Label="DigraphLongestSimpleCircuit">
Expand Down
2 changes: 2 additions & 0 deletions gap/oper.gd
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ DeclareOperation("DigraphShortestDistance", [IsDigraph, IsList]);
DeclareOperation("DigraphShortestPath", [IsDigraph, IsPosInt, IsPosInt]);
DeclareOperation("DigraphShortestPathSpanningTree", [IsDigraph, IsPosInt]);
DeclareOperation("VerticesReachableFrom", [IsDigraph, IsPosInt]);
DeclareOperation("Dominators", [IsDigraph, IsPosInt]);
DeclareOperation("DominatorTree", [IsDigraph, IsPosInt]);

# 10. Operations for vertices . . .
DeclareOperation("PartialOrderDigraphJoinOfVertices",
Expand Down
144 changes: 142 additions & 2 deletions gap/oper.gi
Original file line number Diff line number Diff line change
Expand Up @@ -1829,8 +1829,8 @@ function(D, root)
have_visited_root;
N := DigraphNrVertices(D);
if 0 = root or root > N then
ErrorNoReturn("the 2nd argument (root)",
" is not a vertex of the 1st argument (a digraph)");
ErrorNoReturn("the 2nd argument (root) is not a vertex of the 1st ",
"argument (a digraph)");
fi;
index := ListWithIdenticalEntries(N, 0);
have_visited_root := false;
Expand Down Expand Up @@ -1863,6 +1863,146 @@ function(D, root)
return visited;
end);

InstallMethod(DominatorTree, "for a digraph and a vertex",
[IsDigraph, IsPosInt],
function(D, root)
local M, node_to_preorder_num, preorder_num_to_node, parent, index, next,
current, succ, prev, n, semi, lastlinked, label, bucket, idom,
compress, eval, pred, N, w, y, x, i, v;
M := DigraphNrVertices(D);

if 0 = root or root > M then
ErrorNoReturn("the 2nd argument (root) is not a vertex of the 1st ",
"argument (a digraph)");
fi;

node_to_preorder_num := [];
node_to_preorder_num[root] := 1;
preorder_num_to_node := [root];

parent := [];
parent[root] := fail;

index := ListWithIdenticalEntries(M, 1);

next := 2;
current := root;
succ := OutNeighbours(D);
repeat
prev := current;
for i in [index[current] .. Length(succ[current])] do
n := succ[current][i];
if not IsBound(node_to_preorder_num[n]) then
Add(preorder_num_to_node, n);
parent[n] := current;
index[current] := i + 1;
node_to_preorder_num[n] := next;
next := next + 1;
current := n;
break;
fi;
od;
if prev = current then
current := parent[current];
fi;
until current = fail;
semi := [1 .. M];
lastlinked := M + 1;
label := [];
bucket := List([1 .. M], x -> []);
idom := [];
idom[root] := root;

compress := function(v)
local u;
u := parent[v];
if u <> fail and lastlinked <= M and node_to_preorder_num[u] >=
node_to_preorder_num[lastlinked] then
compress(u);
if node_to_preorder_num[semi[label[u]]]
< node_to_preorder_num[semi[label[v]]] then
label[v] := label[u];
fi;
parent[v] := parent[u];
fi;
end;

eval := function(v)
if lastlinked <= M and node_to_preorder_num[v] >=
node_to_preorder_num[lastlinked] then
compress(v);
return label[v];
else
return v;
fi;
end;

pred := InNeighbours(D);
N := Length(preorder_num_to_node);
for i in [N, N - 1 .. 2] do
w := preorder_num_to_node[i];
for v in bucket[w] do
y := eval(v);
if node_to_preorder_num[semi[y]] < node_to_preorder_num[w] then
idom[v] := y;
else
idom[v] := w;
fi;
od;
bucket[w] := [];
for v in pred[w] do
if IsBound(node_to_preorder_num[v]) then
x := eval(v);
if node_to_preorder_num[semi[x]] < node_to_preorder_num[semi[w]] then
semi[w] := semi[x];
fi;
fi;
od;
if parent[w] = semi[w] then
idom[w] := parent[w];
else
Add(bucket[semi[w]], w);
fi;
lastlinked := w;
label[w] := semi[w];
od;
for v in bucket[root] do
idom[v] := root;
od;
for i in [2 .. N] do
w := preorder_num_to_node[i];
if idom[w] <> semi[w] then
idom[w] := idom[semi[w]];
fi;
od;
idom[root] := fail;
return rec(idom := idom, preorder := preorder_num_to_node);
end);

InstallMethod(Dominators, "for a digraph and a vertex",
[IsDigraph, IsPosInt],
function(D, root)
local tree, preorder, result, u, v;
if not root in DigraphVertices(D) then
ErrorNoReturn("the 2nd argument (a pos. int.) is not a vertex of ",
"the 1st argument (a digraph)");
fi;
tree := DominatorTree(D, root);
preorder := tree.preorder;
tree := tree.idom;
result := [];
for v in preorder do
u := tree[v];
if u <> fail then
result[v] := [u];
if IsBound(result[u]) then
Append(result[v], result[u]);
fi;
fi;
od;
return result;
end);

#############################################################################
# 10. Operations for vertices
#############################################################################
Expand Down
Loading

0 comments on commit ffc9ed8

Please sign in to comment.