Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Tarjan and Lengauer's (almost) linear time dominators algorithm #336

Merged
merged 2 commits into from
Mar 3, 2021
Merged
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
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 := "mam49@st-andrews.ac.uk",
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 := "seh25@st-andrews.ac.uk",
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 @@ -99,6 +99,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)
wilfwilson marked this conversation as resolved.
Show resolved Hide resolved
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]];
james-d-mitchell marked this conversation as resolved.
Show resolved Hide resolved
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)
wilfwilson marked this conversation as resolved.
Show resolved Hide resolved
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