Skip to content

Commit d28eeea

Browse files
committed
Update printing -> only visit each node once + shorten huge graphs
1 parent 3171d20 commit d28eeea

5 files changed

Lines changed: 85 additions & 51 deletions

File tree

src/component_models/ModelList.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,6 @@ function ModelList(
187187
ts_kwargs = homogeneous_ts_kwargs(status)
188188
ts_kwargs = add_model_vars(ts_kwargs, mods, type_promotion)
189189

190-
191-
192190
model_list = ModelList(
193191
mods,
194192
ts_kwargs,

src/dependencies/dependency_graph.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ AbstractTrees.printnode(io::IO, node::HardDependencyNode{T}) where {T} = print(i
6464
AbstractTrees.printnode(io::IO, node::SoftDependencyNode{T}) where {T} = print(io, T)
6565
Base.show(io::IO, t::AbstractDependencyNode) = AbstractTrees.print_tree(io, t)
6666
Base.length(t::AbstractDependencyNode) = length(collect(AbstractTrees.PreOrderDFS(t)))
67+
Base.length(t::DependencyGraph) = length(traverse_dependency_graph(t))
68+
AbstractTrees.children(t::DependencyGraph) = collect(t.roots)
6769

6870
# Long form printing
6971
function Base.show(io::IO, ::MIME"text/plain", t::DependencyGraph)

src/dependencies/printing.jl

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
11
function draw_dependency_graph(
22
io,
33
graphs::DependencyGraph;
4-
title="Dependency graph",
4+
title=string("Dependency graph (", length(graphs), " models)"),
5+
max_depth::Int=1,
56
title_style::String="#FFA726 italic",
67
guides_style::String="#42A5F5",
78
dep_graph_guides=(space=" ", vline="", branch="", leaf="", hline="")
89
)
910

1011
dep_graph_guides = map((g) -> Term.apply_style("{$guides_style}$g{/$guides_style}"), dep_graph_guides)
1112

13+
max_depth_reached = Ref(max_depth)
14+
1215
graph_panel = []
1316
for (p, graph) in graphs.roots
17+
if max_depth_reached[] <= 0
18+
push!(graph_panel, "...")
19+
break
20+
end
1421
node = []
1522
# p = :process2; graph = graphs.roots[p]
1623
# typeof(deps[:process4].children[1].hard_dependency.children[1])
17-
draw_panel(node, graph, "", dep_graph_guides, graph; title="Main model")
24+
draw_panel(node, graph, "", dep_graph_guides, graph, max_depth_reached; title="Main model")
1825
push!(graph_panel, Term.Panel(node...; fit=true, title=string(p), style="green dim"))
26+
max_depth_reached[] -= 1
1927
end
2028

2129
print(
@@ -34,7 +42,12 @@ end
3442
3543
Draw the panels for all dependencies
3644
"""
37-
function draw_panel(node, graph, prefix, dep_graph_guides, parent; title="Soft-coupled model")
45+
function draw_panel(node, graph, prefix, dep_graph_guides, parent, max_depth_reached=Ref(5); title="Soft-coupled model")
46+
47+
if max_depth_reached[] <= 0
48+
push!(node, "...")
49+
return nothing
50+
end
3851

3952
# If the node has a sibling, draw a branching guide + a horizontal line:
4053
if length(parent.children) <= 1
@@ -65,7 +78,8 @@ function draw_panel(node, graph, prefix, dep_graph_guides, parent; title="Soft-c
6578
if graph isa SoftDependencyNode
6679
# draw a branching guide if there's more soft dependencies after this one:
6780
for child in graph.hard_dependency
68-
draw_panel(node, child, panel_hright, dep_graph_guides, graph; title="Hard-coupled model")
81+
draw_panel(node, child, panel_hright, dep_graph_guides, graph, max_depth_reached; title="Hard-coupled model")
82+
max_depth_reached[] -= 1
6983
end
7084
elseif isa(parent, SoftDependencyNode) && length(parent.children) > 0
7185
# The current node is a hard dependency of a soft dependency.
@@ -75,7 +89,8 @@ function draw_panel(node, graph, prefix, dep_graph_guides, parent; title="Soft-c
7589

7690
# Recursive call:
7791
for child in AbstractTrees.children(graph)
78-
draw_panel(node, child, panel_hright, dep_graph_guides, graph; title=title_panel(child))
92+
draw_panel(node, child, panel_hright, dep_graph_guides, graph, max_depth_reached; title=title_panel(child))
93+
max_depth_reached[] -= 1
7994
end
8095
end
8196

src/dependencies/soft_dependencies.jl

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ function soft_dependencies(d::DependencyGraph{Dict{Symbol,HardDependencyNode}},
4040
d_vars = Dict{Symbol,Vector{Pair{Symbol,NamedTuple}}}()
4141
for (procname, node) in d.roots
4242
var = Pair{Symbol,NamedTuple}[]
43-
traverse_dependency_graph!(node, variables, var)
43+
nodes_visited = Set{AbstractDependencyNode}()
44+
traverse_dependency_graph!(node, variables, var; node_visited=nodes_visited)
4445
push!(d_vars, procname => var)
4546
end
4647

@@ -138,7 +139,7 @@ function soft_dependencies(d::DependencyGraph{Dict{Symbol,HardDependencyNode}},
138139
end
139140

140141
# For multiscale mapping:
141-
function soft_dependencies_multiscale(soft_dep_graphs_roots::DependencyGraph{Dict{String,Any}}, mapping::Dict{String,A}, hard_dep_dict::Dict{Pair{Symbol, String}, HardDependencyNode}) where {A<:Any}
142+
function soft_dependencies_multiscale(soft_dep_graphs_roots::DependencyGraph{Dict{String,Any}}, mapping::Dict{String,A}, hard_dep_dict::Dict{Pair{Symbol,String},HardDependencyNode}) where {A<:Any}
142143
mapped_vars = mapped_variables(mapping, soft_dep_graphs_roots, verbose=false)
143144
rev_mapping = reverse_mapping(mapped_vars, all=false)
144145

@@ -151,11 +152,11 @@ function soft_dependencies_multiscale(soft_dep_graphs_roots::DependencyGraph{Dic
151152

152153
# Remove the hard dependencies from the soft dependencies:
153154
soft_deps_not_hard = drop_process(soft_deps, [hd.process for hd in i.hard_dependency])
154-
155+
155156
hard_dependencies_from_other_scale = [hd for hd in i.hard_dependency if hd.scale != i.scale]
156157

157158
# NB: if a node is already a hard dependency of the node, it cannot be a soft dependency
158-
159+
159160
# Check if the process has soft dependencies at other scales:
160161
soft_deps_multiscale = search_inputs_in_multiscale_output(proc, organ, ins, soft_dep_graphs_roots.roots, rev_mapping, hard_dependencies_from_other_scale)
161162
# Example output: "Soil" => Dict(:soil_water=>[:soil_water_content]), which means that the variable :soil_water_content
@@ -171,37 +172,37 @@ function soft_dependencies_multiscale(soft_dep_graphs_roots::DependencyGraph{Dic
171172
# If the process has soft dependencies, then it is not independant
172173
# and we need to add its parent(s) to the node, and the node as a child
173174
for (parent_soft_dep, soft_dep_vars) in pairs(soft_deps_not_hard)
174-
175+
175176
# if the parent isn't registered as a soft dependency, it likely means the soft dependecy should be to an internal hard dependency to the parent
176-
if(!haskey(soft_dep_graph, parent_soft_dep))
177+
if (!haskey(soft_dep_graph, parent_soft_dep))
177178

178179
roots_at_given_scale = soft_dep_graphs_roots.roots[i.scale][:soft_dep_graph]
179-
if !(parent_soft_dep in keys(roots_at_given_scale))
180+
if !(parent_soft_dep in keys(roots_at_given_scale))
180181
master_node = ()
181-
for ((hd_key, hd_scale), hd) in hard_dep_dict
182-
if parent_soft_dep == hd_key
183-
master_node = hd
182+
for ((hd_key, hd_scale), hd) in hard_dep_dict
183+
if parent_soft_dep == hd_key
184+
master_node = hd
184185
depth = 0
185186
# A cleaner way of preventing cycles or infinite loops would be more desirable
186187
while !isa(master_node, SoftDependencyNode) && depth < 50
187188
master_node.parent === nothing && error("Finalised hard dependency has no parent")
188189
master_node = master_node.parent
189190
depth += 1
190-
end
191-
191+
end
192+
192193
break
193194
end
194-
end
195-
master_node == () && error("Parent is not located in hard deps, nor in roots, which should be the case when initalizing soft dependencies")
196-
end
195+
end
196+
master_node == () && error("Parent is not located in hard deps, nor in roots, which should be the case when initalizing soft dependencies")
197+
end
197198
# NOTE : this may need to be propagated within internal hard dependencies' ancestors of this model... ?
198199
parent_node = soft_dep_graphs_roots.roots[master_node.scale][:soft_dep_graph][master_node.process]
199200
else
200201
parent_node = soft_dep_graph[parent_soft_dep]
201202
end
202-
203-
204-
203+
204+
205+
205206
# preventing a cyclic dependency
206207
if parent_soft_dep == proc
207208
error("Cyclic model dependency detected for process $proc from organ $organ.")
@@ -247,33 +248,33 @@ function soft_dependencies_multiscale(soft_dep_graphs_roots::DependencyGraph{Dic
247248
# If the node has soft dependencies at other scales, add it as child of the other scale (and add its parent too):
248249
if length(soft_deps_multiscale) > 0
249250
for org in keys(soft_deps_multiscale)
250-
for (parent_soft_dep, soft_dep_vars) in soft_deps_multiscale[org]
251-
251+
for (parent_soft_dep, soft_dep_vars) in soft_deps_multiscale[org]
252+
252253
# if the node has a soft dependency on a node that is a nested hard dependency,
253254
# have it point to the master node of that hard dependency instead of the internal node
254255
# This check is meant in case the organ at the inspected scale is part of a hard dependency,
255256
# and therefore already absent from the roots
256257

257-
roots_at_given_scale = soft_dep_graphs_roots.roots[org][:soft_dep_graph]
258-
if !(parent_soft_dep in keys(roots_at_given_scale))
258+
roots_at_given_scale = soft_dep_graphs_roots.roots[org][:soft_dep_graph]
259+
if !(parent_soft_dep in keys(roots_at_given_scale))
259260
master_node = ()
260-
for ((hd_key, hd_scale), hd) in hard_dep_dict
261-
if parent_soft_dep == hd_key
262-
master_node = hd
261+
for ((hd_key, hd_scale), hd) in hard_dep_dict
262+
if parent_soft_dep == hd_key
263+
master_node = hd
263264
depth = 0
264265
# A cleaner way of preventing cycles or infinite loops would be more desirable
265266
while !isa(master_node, SoftDependencyNode) && depth < 50
266267
master_node.parent === nothing && error("Finalised hard dependency has no parent")
267268
master_node = master_node.parent
268269
depth += 1
269-
end
270-
270+
end
271+
271272
break
272273
end
273-
end
274+
end
274275

275276
master_node == () && error("Parent is not located in hard deps, nor in roots, which should be the case when initalizing soft dependencies")
276-
277+
277278
# NOTE : this may need to be propagated within internal hard dependencies' ancestors of this model... ?
278279
parent_node = soft_dep_graphs_roots.roots[master_node.scale][:soft_dep_graph][master_node.process]
279280
else
@@ -298,13 +299,13 @@ function soft_dependencies_multiscale(soft_dep_graphs_roots::DependencyGraph{Dic
298299
)
299300
end
300301

301-
302+
302303
if !(i in parent_node.children) # && error("Cyclic dependency detected for process $proc from organ $organ.")
303304

304305
# Add the current node as a child of the node on which it depends:
305306
push!(parent_node.children, i)
306307
end
307-
# Add the node on which the current node depends as a parent
308+
# Add the node on which the current node depends as a parent
308309
if i.parent === nothing
309310
# If the node had no parent already, it is nothing, so we change into a vector
310311
i.parent = [parent_node]
@@ -315,7 +316,7 @@ function soft_dependencies_multiscale(soft_dep_graphs_roots::DependencyGraph{Dic
315316
end
316317

317318
# Add the multiscale soft dependencies variables of the parent to the current node
318-
i.parent_vars = NamedTuple(Symbol(k) => NamedTuple(v) for (k, v) in soft_deps_multiscale)
319+
i.parent_vars = NamedTuple(Symbol(k) => NamedTuple(v) for (k, v) in soft_deps_multiscale)
319320
end
320321
end
321322
end
@@ -488,9 +489,9 @@ function search_inputs_in_multiscale_output(process, organ, inputs, soft_dep_gra
488489
# Avoid collecting variables at other scales if they come from a hard dependency
489490
# They are handled internally by the hard dep, so if a hard dependency contains that variable, don't add it
490491
# (This only needs to be done one level beneath the soft dependency nodes, any hard dependencies internal to another one don't expose their variables here)
491-
492+
492493
in_hard_dep::Bool = false
493-
hd_os_current_scale = filter(x -> x.scale == org, hard_dependencies_from_other_scale)
494+
hd_os_current_scale = filter(x -> x.scale == org, hard_dependencies_from_other_scale)
494495
for hd_os in hd_os_current_scale
495496
hd_os_output_vars = [first(p) for p in pairs(hd_os.outputs)]
496497
in_hard_dep |= length(filter(x -> x == var, hd_os_output_vars)) > 0

src/dependencies/traversal.jl

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
"""
22
traverse_dependency_graph(graph::DependencyGraph, f::Function; visit_hard_dep=true)
3+
traverse_dependency_graph(graph; visit_hard_dep=true)
34
4-
Traverse the dependency `graph` and apply the function `f` to each node.
5-
The first-level soft-dependencies are traversed first, then their
6-
hard-dependencies (if `visit_hard_dep=true`), and then the children of the soft-dependencies.
5+
Traverse the dependency `graph` and apply the function `f` to each node, or just return the nodes if `f` is not provided.
6+
7+
The first-level soft-dependencies are traversed first, then their hard-dependencies (if `visit_hard_dep=true`), and then
8+
the children of the soft-dependencies.
79
810
Return a vector of pairs of the node and the result of the function `f`.
911
@@ -38,23 +40,38 @@ function traverse_dependency_graph(
3840
f::Function;
3941
visit_hard_dep=true
4042
)
41-
var = []
42-
node_visited = Set()
43+
44+
var = Pair{Symbol,Any}[]
45+
nodes_types = visit_hard_dep ? AbstractDependencyNode : SoftDependencyNode
46+
node_visited = Set{nodes_types}()
4347
for (p, root) in graph.roots
4448
traverse_dependency_graph!(root, f, var; visit_hard_dep=visit_hard_dep, node_visited=node_visited)
4549
end
4650

4751
return var
4852
end
4953

54+
function traverse_dependency_graph(
55+
graph::DependencyGraph,
56+
visit_hard_dep=true
57+
)
58+
nodes_types = visit_hard_dep ? AbstractDependencyNode : SoftDependencyNode
59+
var = Pair{Symbol,nodes_types}[]
60+
node_visited = Set{nodes_types}()
61+
for (p, root) in graph.roots
62+
traverse_dependency_graph!(root, x -> x, var; visit_hard_dep=visit_hard_dep, node_visited=node_visited)
63+
end
5064

51-
function traverse_dependency_graph!(f::Function, graph::DependencyGraph; visit_hard_dep=true, node_visited::Set{AbstractDependencyNode}=Set())
65+
return last.(var)
66+
end
67+
68+
function traverse_dependency_graph!(f::Function, graph::DependencyGraph; visit_hard_dep=true, node_visited::Set=Set{AbstractDependencyNode}())
5269
for (p, root) in graph.roots
5370
traverse_dependency_graph!(f, root, visit_hard_dep=visit_hard_dep, node_visited=node_visited)
5471
end
5572
end
5673

57-
function traverse_dependency_graph!(f::Function, node::SoftDependencyNode; visit_hard_dep=true, node_visited::Set{AbstractDependencyNode}=Set())
74+
function traverse_dependency_graph!(f::Function, node::SoftDependencyNode; visit_hard_dep=true, node_visited::Set=Set{AbstractDependencyNode}())
5875
if node in node_visited
5976
return nothing
6077
end
@@ -76,7 +93,7 @@ function traverse_dependency_graph!(f::Function, node::SoftDependencyNode; visit
7693
end
7794
end
7895

79-
function traverse_dependency_graph!(f::Function, node::HardDependencyNode; visit_hard_dep=true, node_visited::Set{AbstractDependencyNode}=Set())
96+
function traverse_dependency_graph!(f::Function, node::HardDependencyNode; visit_hard_dep=true, node_visited::Set=Set{AbstractDependencyNode}())
8097
if node in node_visited
8198
return nothing
8299
end
@@ -105,7 +122,7 @@ function traverse_dependency_graph!(
105122
f::Function,
106123
var::Vector;
107124
visit_hard_dep=true,
108-
node_visited::Set{AbstractDependencyNode}=Set()
125+
node_visited::Set=Set{AbstractDependencyNode}()
109126
)
110127
if node in node_visited
111128
return nothing
@@ -140,8 +157,9 @@ function traverse_dependency_graph!(
140157
f::Function,
141158
var::Vector;
142159
visit_hard_dep=true, # Just to be compatible with a call shared with SoftDependencyNode method
143-
node_visited::Set{AbstractDependencyNode}=Set()
160+
node_visited::Set=Set{HardDependencyNode}()
144161
)
162+
145163
if node in node_visited
146164
return nothing
147165
end

0 commit comments

Comments
 (0)