Skip to content

Commit 1f40e0f

Browse files
authored
Add DAGs (#5)
* Add basic DAG construction * DAG improvements, but gets confused with :block * Working DAGs! * Rename LabelledTree -> LabelledDigraph * Refactor into separate files * Export at-dag_cse * Add test for at-dag_cse * Reduce examples to single notebook * Add CommonSubexpressions to REQUIRE * Add source files
1 parent ebfe75d commit 1f40e0f

File tree

8 files changed

+675
-348
lines changed

8 files changed

+675
-348
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ which gives the following output:
2121

2222
![example_tree](example_tree.png)
2323

24-
See [this notebook](examples/TreeView.ipynb) for usage examples.
24+
See [this notebook](examples/TreeView usage.ipynb) for usage examples.
2525

2626
## Installation prerequisites
2727

REQUIRE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ julia 0.5
22
LightGraphs 0.7
33
TikzGraphs 0.3
44
MacroTools 0.3
5+
CommonSubexpressions

examples/TreeView.ipynb renamed to examples/TreeView usage.ipynb

Lines changed: 397 additions & 252 deletions
Large diffs are not rendered by default.

src/TreeView.jl

Lines changed: 10 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -2,111 +2,30 @@ module TreeView
22

33
using LightGraphs, TikzGraphs
44
using MacroTools
5+
using CommonSubexpressions
56

67
export LabelledTree, walk_tree, walk_tree!, draw, @tree, @tree_with_call,
78
tikz_representation
89

9-
immutable LabelledTree
10-
g::Graph
11-
labels::Vector{String}
12-
end
13-
14-
add_numbered_vertex!(g) = (add_vertex!(g); top = nv(g)) # returns the number of the new vertex
15-
16-
# latex treats # as a special character, so we have to escape it. See:
17-
# https://github.com/sisl/TikzGraphs.jl/issues/12
18-
latex_escape(s::String) = replace(s, "#", "\\#")
19-
20-
"Convert the current node into a label"
21-
function label(sym)
22-
sym == :(^) && return "\\textasciicircum" # TikzGraphs chokes on ^
23-
24-
return latex_escape(string("\\texttt{", sym, "}"))
25-
end
26-
27-
28-
"""
29-
walk_tree!(g, labels, ex, show_call=true)
30-
31-
Walk the abstract syntax tree (AST) of the given expression `ex`.
32-
Builds up the graph `g` and the set of `labels`
33-
for each node, both modified in place
34-
35-
`show_call` specifies whether to include `call` nodes in the graph.
36-
Including them represents the Julia AST more precisely, but adds visual noise.
37-
38-
Returns the number of the top vertex.
39-
"""
40-
41-
function walk_tree!(g, labels, ex, show_call=true)
42-
43-
top_vertex = add_numbered_vertex!(g)
44-
45-
start_argument = 1 # which argument to start with
46-
47-
if !(show_call) && ex.head == :call
48-
f = ex.args[1] # the function name
49-
push!(labels, label(f))
50-
51-
start_argument = 2 # drop "call" from tree
52-
53-
else
54-
push!(labels, label(ex.head))
55-
end
10+
export make_dag, @dag, @dag_cse
5611

5712

58-
for i in start_argument:length(ex.args)
13+
abstract LabelledDiGraph
5914

60-
if isa(ex.args[i], Expr)
61-
62-
child = walk_tree!(g, labels, ex.args[i], show_call)
63-
add_edge!(g, top_vertex, child)
64-
65-
else
66-
n = add_numbered_vertex!(g)
67-
add_edge!(g, top_vertex, n)
68-
69-
push!(labels, label(ex.args[i]))
70-
71-
end
72-
end
73-
74-
return top_vertex
75-
76-
end
77-
78-
function walk_tree(ex::Expr, show_call=false)
79-
g = Graph()
80-
labels = String[]
81-
82-
walk_tree!(g, labels, ex, show_call)
83-
84-
return LabelledTree(g, labels)
85-
86-
end
87-
88-
tikz_representation(tree) = TikzGraphs.plot(tree.g, tree.labels)
89-
90-
import Base.show
91-
function show(io::IO, mime::MIME"image/svg+xml", tree::LabelledTree)
92-
p = tikz_representation(tree) # TikzPicture object
93-
show(io, mime, p)
15+
immutable LabelledTree <: LabelledDiGraph
16+
g::DiGraph
17+
labels::Vector{Any}
9418
end
9519

20+
add_numbered_vertex!(g) = (add_vertex!(g); top = nv(g)) # returns the number of the new vertex
9621

9722

98-
function draw(tree::LabelledTree)
99-
TikzGraphs.plot(tree.g, tree.labels)
100-
end
10123

24+
include("tree.jl")
25+
include("dag.jl")
26+
include("display.jl")
10227

103-
macro tree(ex::Expr)
104-
walk_tree(ex)
105-
end
10628

107-
macro tree_with_call(ex::Expr)
108-
walk_tree(ex, true)
109-
end
11029

11130

11231
end # module

src/dag.jl

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# Make a DAG (Directed Acyclic Graph) by storing references to each symbol
2+
3+
"""
4+
Structure representing a DAG.
5+
Maintains a `symbol_map` giving the currently-known symbols and the corresponding
6+
vertex number in the graph.
7+
"""
8+
immutable DirectedAcyclicGraph <: LabelledDiGraph
9+
g::DiGraph
10+
labels::Vector{Any}
11+
symbol_map::Dict{Symbol, Int}
12+
end
13+
14+
DirectedAcyclicGraph() = DirectedAcyclicGraph(DiGraph(), Symbol[], Dict())
15+
16+
"""
17+
Adds a symbol to the DAG if it doesn't already exist.
18+
Returns the vertex number
19+
"""
20+
21+
# Make numbers unique:
22+
function add_symbol!(dag::DirectedAcyclicGraph, s) # number
23+
vertex = add_numbered_vertex!(dag.g)
24+
push!(dag.labels, s)
25+
return vertex
26+
end
27+
28+
function lookup!(dag::DirectedAcyclicGraph, s)
29+
add_symbol!(dag, s)
30+
end
31+
32+
"""
33+
Look up a symbol to see if it has already been seen.
34+
"""
35+
function lookup!(dag::DirectedAcyclicGraph, s::Symbol)
36+
if haskey(dag.symbol_map, s)
37+
return dag.symbol_map[s]
38+
39+
else # make new one:
40+
vertex = add_numbered_vertex!(dag.g)
41+
push!(dag.labels, s)
42+
dag.symbol_map[s] = vertex
43+
return vertex
44+
end
45+
end
46+
47+
48+
make_dag!(dag::DirectedAcyclicGraph, s) = lookup!(dag, s)
49+
50+
"""
51+
Update a Directed Acyclic Graph with the result of traversing the given `Expr`ession.
52+
"""
53+
function make_dag!(dag::DirectedAcyclicGraph, ex::Expr)
54+
55+
local top
56+
57+
if ex.head == :block
58+
for arg in ex.args
59+
make_dag!(dag, arg)
60+
end
61+
return -1
62+
63+
elseif ex.head == :(=) # treat assignment as just giving pointers to the tree
64+
local_var = ex.args[1]
65+
66+
top = make_dag!(dag, ex.args[2])
67+
68+
dag.symbol_map[local_var] = top # add an alias to the corresponding tree node
69+
70+
return top
71+
72+
end
73+
74+
75+
where_start = 1 # which argument to start with
76+
77+
if ex.head == :call
78+
f = ex.args[1] # the function name
79+
top = add_symbol!(dag, f)
80+
81+
where_start = 2 # drop "call" from tree
82+
83+
84+
else
85+
@show ex.head
86+
top = add_symbol!(dag, ex.head)
87+
end
88+
89+
# @show top
90+
91+
for arg in ex.args[where_start:end]
92+
93+
# @show arg, typeof(arg)
94+
95+
if isa(arg, Expr)
96+
97+
child = make_dag!(dag, arg)
98+
# @show "Expr", top, child
99+
add_edge!(dag.g, top, child)
100+
101+
else
102+
child = lookup!(dag, arg)
103+
# @show top, child
104+
add_edge!(dag.g, top, child)
105+
106+
end
107+
end
108+
109+
return top
110+
111+
end
112+
113+
"""
114+
Make a Directed Acyclic Graph (DAG) from a Julia expression.
115+
"""
116+
function make_dag(ex::Expr)
117+
118+
dag = DirectedAcyclicGraph()
119+
120+
make_dag!(dag, MacroTools.striplines(ex))
121+
122+
return dag
123+
124+
end
125+
126+
"""
127+
Make a Directed Acyclic Graph (DAG) from a Julia expression.
128+
"""
129+
macro dag(ex::Expr)
130+
make_dag(ex)
131+
end
132+
133+
"""
134+
Perform common subexpression elimination on a Julia `Expr`ession,
135+
and make a Directed Acyclic Graph (DAG) of the result.
136+
"""
137+
macro dag_cse(ex::Expr)
138+
make_dag(cse(ex)) # common subexpression elimination
139+
end
140+
141+
142+
import Base.show
143+
function show(io::IO, mime::MIME"image/svg+xml", dag::DirectedAcyclicGraph)
144+
p = tikz_representation(dag) # TikzPicture object
145+
show(io, mime, p)
146+
end

src/display.jl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
2+
# latex treats # as a special character, so we have to escape it. See:
3+
# https://github.com/sisl/TikzGraphs.jl/issues/12
4+
5+
latex_escape(s::String) = replace(s, "#", "\\#")
6+
7+
"Convert a symbol or into a LaTeX label"
8+
function latex_label(sym)
9+
sym == :(^) && return "\\textasciicircum" # TikzGraphs chokes on ^
10+
11+
return latex_escape(string("\\texttt{", sym, "}"))
12+
end
13+
14+
15+
"""
16+
Return a Tikz representation of a tree object.
17+
The tree object must have fields `g` (the graph) and `labels`.
18+
"""
19+
function tikz_representation(tree::LabelledDiGraph)
20+
labels = String[latex_label(x) for x in tree.labels]
21+
return TikzGraphs.plot(tree.g, labels)
22+
end
23+
24+
25+
function Base.show(io::IO, mime::MIME"image/svg+xml", tree::LabelledTree)
26+
27+
p = tikz_representation(tree) # TikzPicture object
28+
show(io, mime, p)
29+
30+
end

src/tree.jl

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"""
2+
walk_tree!(g, labels, ex, show_call=true)
3+
4+
Walk the abstract syntax tree (AST) of the given expression `ex`.
5+
Builds up the graph `g` and the set of `labels`
6+
for each node, both modified in place
7+
8+
`show_call` specifies whether to include `call` nodes in the graph.
9+
Including them represents the Julia AST more precisely, but adds visual noise.
10+
11+
Returns the number of the top vertex.
12+
"""
13+
14+
function walk_tree!(g, labels, ex, show_call=true)
15+
16+
top_vertex = add_numbered_vertex!(g)
17+
18+
where_start = 1 # which argument to start with
19+
20+
if !(show_call) && ex.head == :call
21+
f = ex.args[1] # the function name
22+
push!(labels, f)
23+
24+
where_start = 2 # drop "call" from tree
25+
26+
else
27+
push!(labels, ex.head)
28+
end
29+
30+
31+
for i in where_start:length(ex.args)
32+
33+
if isa(ex.args[i], Expr)
34+
35+
child = walk_tree!(g, labels, ex.args[i], show_call)
36+
add_edge!(g, top_vertex, child)
37+
38+
else
39+
n = add_numbered_vertex!(g)
40+
add_edge!(g, top_vertex, n)
41+
42+
push!(labels, ex.args[i])
43+
44+
end
45+
end
46+
47+
return top_vertex
48+
49+
end
50+
51+
function walk_tree(ex::Expr, show_call=false)
52+
g = DiGraph()
53+
labels = Any[]
54+
55+
walk_tree!(g, labels, ex, show_call)
56+
57+
return LabelledTree(g, labels)
58+
59+
end
60+
61+
"""
62+
Make a tree from a Julia `Expr`ession.
63+
Omits `call`.
64+
"""
65+
macro tree(ex::Expr)
66+
walk_tree(ex)
67+
end
68+
69+
"""
70+
Make a tree from a Julia `Expr`ession.
71+
Includes `call`.
72+
"""
73+
macro tree_with_call(ex::Expr)
74+
walk_tree(ex, true)
75+
end

0 commit comments

Comments
 (0)