Skip to content

Commit

Permalink
oper: add dominators algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
james-d-mitchell committed Dec 24, 2020
1 parent 8d46bc4 commit db33f03
Show file tree
Hide file tree
Showing 8 changed files with 527 additions and 4 deletions.
4 changes: 0 additions & 4 deletions .appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
# set clone depth
clone_depth: 1 # clone entire repository history if not defined

branches:
except:
- /rc-v\d+\.\d+\.\d+/

environment:
TEST_SUITE: test
PACKAGES: "latest"
Expand Down
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 = {http://dx.doi.org/10.1016/j.jsc.2013.09.003},
Expand Down
64 changes: 64 additions & 0 deletions doc/oper.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1572,6 +1572,70 @@ 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>
Dominators is a function that takes a <A>digraph</A> and a specific root
<A>vertex</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 fast algorith written 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 O(mlog n).
<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.
<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
219 changes: 219 additions & 0 deletions gap/oper.gi
Original file line number Diff line number Diff line change
Expand Up @@ -1725,6 +1725,225 @@ function(D, list)
return DigraphShortestDistance(D, list[1], list[2]);
end);

# This function is left here only for testing purposes, for the
# faster version of dominators.
# InstallMethod(DominatorsQuadratic, "for a digraph and vertex",
# [IsDigraph, IsPosInt],
# function(D, root)
# local N, seen, parent, index, next, current, succ, prev, n, comp,
# predecessors, dominators, changes, intersection, i, v, pred;

# Figure out what nodes are reachable from the root
# TODO this should be a separate function VerticesReachableFrom?
# N := DigraphNrVertices(D);

# if root > N then
# ErrorNoReturn("Error, the 2nd argument <root> is not a ",
# "vertex of the 1st argument <D>,");
# fi;

# seen := BlistList([1 .. N], [root]);

# parent := [];
# parent[root] := fail;
# index := ListWithIdenticalEntries(N, 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 seen[n] then
# seen[n] := true;
# parent[n] := current;
# index[current] := i + 1;
# next := next + 1;
# current := n;
# break;
# fi;
# od;
# continues from here
# if prev = current then
# we backtrack
# current := parent[current];
# fi;
# until current = fail;

# comp := ListBlist([1 .. N], seen);
# predecessors := InNeighbours(D);

# dominators := List(DigraphVertices(D), x -> []);
# dominators{comp} := List(comp, x -> ShallowCopy(comp));
# dominators[root] := [root];

# repeat
# changes := false;
# for v in comp do
# if v <> root then
# if not IsEmpty(predecessors[v]) then
# intersection := ShallowCopy(comp);
# for pred in predecessors[v] do
# if seen[pred] then
# IntersectSet(intersection, dominators[pred]);
# fi;
# od;
# else
# intersection := [];
# fi;

# AddSet(intersection, v);
# if intersection <> dominators[v] then
# changes := true;
# dominators[v] := intersection;
# fi;
# fi;
# od;
# until changes = false;
# return dominators;
# end);

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

N := DigraphNrVertices(D);

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

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

index := ListWithIdenticalEntries(N, 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 .. N];
label := [];
ancestor := ListWithIdenticalEntries(N, 0);
bucket := List([1 .. N], x -> []);
idom := [];
idom[root] := root;

compress := function(v)
local u;
u := ancestor[v];
if ancestor[u] <> 0 then
compress(u);
if node_to_preorder_num[semi[label[u]]]
< node_to_preorder_num[semi[label[v]]] then
label[v] := label[u];
fi;
ancestor[v] := ancestor[u];
fi;
end;

eval := function(v)
if ancestor[v] <> 0 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;
ancestor[w] := parent[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 db33f03

Please sign in to comment.