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 <mam49@st-andrews.ac.uk>
Co-authored-by: Samantha Harper <seh25@st-andrews.ac.uk>
  • Loading branch information
3 people committed Dec 24, 2020
1 parent 8d46bc4 commit accd1bd
Show file tree
Hide file tree
Showing 7 changed files with 474 additions and 3 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 := "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 @@ -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 = {http://dx.doi.org/10.1016/j.jsc.2013.09.003},
Expand Down
87 changes: 87 additions & 0 deletions doc/oper.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1572,6 +1572,93 @@ 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> is a function that takes a <A>digraph</A> and a
specific root <A>root</A> and returns the dominators of each vertex with
respect to the root. The root must be a verex of the digraph. The algorithm
is an implementation of the almost linear time algorithm by
Thomas Lengauer and Robert Endre Tarjan <Cite Key="LT79"/>. If there is
no path from the root to a specific vertex, the output will contain a
hole in the corresponding position. The complexity of this algorithm is
<M>O(mlog n)</M> where <M>m</M> and <M>n</M> are the
number of edges and number of nodes in the subdigraph induced by the nodes
in <A>digraph</A> reachable from <A>root</A>, respectively.
<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 ], [ 4, 3, 2, 1 ],, [ 6, 2, 1 ] ]
gap> Dominators(D, 2);
[ [ 1, 4, 3, 2 ], [ 2 ], [ 3, 2 ], [ 4, 3, 2 ],, [ 6, 2 ] ]
gap> Dominators(D, 3);
[ [ 1, 4, 3 ], [ 2, 3 ], [ 3 ], [ 4, 3 ],, [ 6, 2, 3 ] ]
gap> Dominators(D, 4);
[ [ 1, 4 ], [ 2, 1, 4 ], [ 3, 2, 1, 4 ], [ 4 ],, [ 6, 2, 1, 4 ] ]
gap> Dominators(D, 5);
[ ,,,, [ 5 ] ]
gap> Dominators(D, 6);
[ [ 1, 4, 3, 6 ], [ 2, 3, 6 ], [ 3, 6 ], [ 4, 3, 6 ],, [ 6 ] ]
]]></Example>
</Description>
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="DominatorTree">
<ManSection>
<Oper Name="DominatorTree" Arg="digraph, root"/>
<Returns>A record.</Returns>
<Description>
<C>DominatorTree</C> is a function that takes a <A>digraph</A> and a specific
<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>
<Mark>sdom</Mark>
<Item>
the semidominators of the vertices with respect to the root.
</Item>
</List>
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> and <M>n</M> are the
number of edges and number of nodes in the subdigraph induced by the nodes
in <A>digraph</A> reachable from <A>root</A>, respectively.

<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 ]
, sdom := [ 1, 1, 1, 1, 4, 2 ] )
gap> DominatorTree(D, 5);
rec( idom := [ 5, 5, 5, 5, fail, 5 ], preorder := [ 5, 1, 2, 4, 3, 6 ]
, sdom := [ 5, 5, 5, 5, 5, 5 ] )
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 ],
sdom := [ 1, 1, 1, 1, 1 ] )
gap> DominatorTree(D, 2);
rec( idom := [ 2, fail, 2, 2, 2 ], preorder := [ 2, 1, 3, 4, 5 ],
sdom := [ 2, 2, 2, 2, 2 ] )
]]></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 @@ -62,6 +62,8 @@
<#Include Label="IsReachable">
<#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 @@ -118,6 +118,8 @@ DeclareOperation("DigraphShortestDistance", [IsDigraph, IsPosInt, IsPosInt]);
DeclareOperation("DigraphShortestDistance", [IsDigraph, IsList, IsList]);
DeclareOperation("DigraphShortestDistance", [IsDigraph, IsList]);
DeclareOperation("DigraphShortestPath", [IsDigraph, IsPosInt, IsPosInt]);
DeclareOperation("Dominators", [IsDigraph, IsPosInt]);
DeclareOperation("DominatorTree", [IsDigraph, IsPosInt]);

# 10. Operations for vertices . . .
DeclareOperation("PartialOrderDigraphJoinOfVertices",
Expand Down
140 changes: 140 additions & 0 deletions gap/oper.gi
Original file line number Diff line number Diff line change
Expand Up @@ -1725,6 +1725,146 @@ function(D, list)
return DigraphShortestDistance(D, list[1], list[2]);
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);

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

# Step 1: DFS to establish preorder
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;
# continues from here
if prev = current then
# we backtrack
current := parent[current];
fi;
until current = fail;

# Step 2: find semidominators, and first pass of immediate dominators
semi := [1 .. M];
lastlinked := M + 1; # never linked
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
# Node is reachable from root
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;

# Step 3: finalize immediate dominators
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, sdom := semi);
end);

InstallMethod(Dominators, "for a digraph and a vertex",
[IsDigraph, IsPosInt],
function(D, root)
local tree, preorder, result, u, v;
tree := DominatorTree(D, root);
preorder := tree.preorder;
tree := tree.idom;
result := [];
for v in preorder do
result[v] := [v];
u := tree[v];
if u <> fail then
Append(result[v], result[u]);
fi;
od;
# Perform(result, Sort);
return result;
end);

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

0 comments on commit accd1bd

Please sign in to comment.