Skip to content

Commit 33a045f

Browse files
committed
Add DeepDiffs.jl support for MatchedSystemStructure.
This utility presents a "unified diff" view of MatchedSystemStructure objects: - Color-highlights any added/removed variables and equations - Color-highlights changes to the dependency graph and the var ⇔ eq matching graph - Does **not** highlight any differences in the solvable sub-graph (`old.solvable_graph`)
1 parent 9f7bfed commit 33a045f

File tree

5 files changed

+176
-18
lines changed

5 files changed

+176
-18
lines changed

Project.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"
4646
UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed"
4747
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
4848

49+
[weakdeps]
50+
DeepDiffs = "ab62b9b5-e342-54a8-a765-a90f495de1a6"
51+
52+
[extensions]
53+
MTKDeepDiffsExt = "DeepDiffs"
54+
4955
[compat]
5056
AbstractTrees = "0.3, 0.4"
5157
ArrayInterface = "6, 7"
@@ -88,6 +94,7 @@ julia = "1.6"
8894
AmplNLWriter = "7c4d4715-977e-5154-bfe0-e096adeac482"
8995
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
9096
ControlSystemsMTK = "687d7614-c7e5-45fc-bfc3-9ee385575c88"
97+
DeepDiffs = "ab62b9b5-e342-54a8-a765-a90f495de1a6"
9198
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
9299
Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9"
93100
Ipopt_jll = "9cc047cb-c261-5740-88fc-0cf96f7bdcc7"

ext/MTKDeepDiffsExt.jl

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
module MTKDeepDiffsExt
2+
3+
using DeepDiffs, ModelingToolkit
4+
using ModelingToolkit.BipartiteGraphs: Label, BipartiteAdjacencyList, unassigned
5+
using ModelingToolkit.SystemStructures: SystemStructure, MatchedSystemStructure,
6+
SystemStructurePrintMatrix, HighlightInt
7+
8+
struct HighlightIntDiff
9+
new::HighlightInt
10+
old::HighlightInt
11+
end
12+
13+
function Base.show(io::IO, d::HighlightIntDiff)
14+
p_color = d.new.highlight
15+
(d.new.match && !d.old.match) && (p_color = :light_green)
16+
(!d.new.match && d.old.match) && (p_color = :light_red)
17+
18+
(d.new.match || d.old.match) && printstyled(io, "(", color = p_color)
19+
if d.new.i != d.old.i
20+
Base.show(io, HighlightInt(d.old.i, :light_red, d.old.match))
21+
print(io, " ")
22+
Base.show(io, HighlightInt(d.new.i, :light_green, d.new.match))
23+
else
24+
Base.show(io, HighlightInt(d.new.i, d.new.highlight, false))
25+
end
26+
(d.new.match || d.old.match) && printstyled(io, ")", color = p_color)
27+
end
28+
29+
struct BipartiteAdjacencyListDiff
30+
new::BipartiteAdjacencyList
31+
old::BipartiteAdjacencyList
32+
end
33+
34+
function Base.show(io::IO, l::BipartiteAdjacencyListDiff)
35+
print(io,
36+
LabelDiff(Label(l.new.match === true ? "" : ""),
37+
Label(l.old.match === true ? "" : "")))
38+
(l.new.match !== true && l.old.match !== true) && print(io, " ")
39+
40+
new_nonempty = isnothing(l.new.u) ? nothing : !isempty(l.new.u)
41+
old_nonempty = isnothing(l.old.u) ? nothing : !isempty(l.old.u)
42+
if new_nonempty === true && old_nonempty === true
43+
if (!isempty(setdiff(l.new.highligh_u, l.new.u)) ||
44+
!isempty(setdiff(l.old.highligh_u, l.old.u)))
45+
throw(ArgumentError("The provided `highligh_u` must be a sub-graph of `u`."))
46+
end
47+
48+
new_items = Dict(i => HighlightInt(i, :nothing, i === l.new.match) for i in l.new.u)
49+
old_items = Dict(i => HighlightInt(i, :nothing, i === l.old.match) for i in l.old.u)
50+
51+
highlighted = union(map(intersect(l.new.u, l.old.u)) do i
52+
HighlightIntDiff(new_items[i], old_items[i])
53+
end,
54+
map(setdiff(l.new.u, l.old.u)) do i
55+
HighlightInt(new_items[i].i, :light_green,
56+
new_items[i].match)
57+
end,
58+
map(setdiff(l.old.u, l.new.u)) do i
59+
HighlightInt(old_items[i].i, :light_red,
60+
old_items[i].match)
61+
end)
62+
print(IOContext(io, :typeinfo => typeof(highlighted)), highlighted)
63+
elseif new_nonempty === true
64+
printstyled(io, map(l.new.u) do i
65+
HighlightInt(i, :nothing, i === l.new.match)
66+
end, color = :light_green)
67+
elseif old_nonempty === true
68+
printstyled(io, map(l.old.u) do i
69+
HighlightInt(i, :nothing, i === l.old.match)
70+
end, color = :light_red)
71+
elseif old_nonempty !== nothing || new_nonempty !== nothing
72+
print(io,
73+
LabelDiff(Label(new_nonempty === false ? "" : "", :light_black),
74+
Label(old_nonempty === false ? "" : "", :light_black)))
75+
else
76+
printstyled(io, '', color = :light_black)
77+
end
78+
end
79+
80+
struct LabelDiff
81+
new::Label
82+
old::Label
83+
end
84+
function Base.show(io::IO, l::LabelDiff)
85+
if l.new != l.old
86+
printstyled(io, l.old.s, color = :light_red)
87+
length(l.new.s) != 0 && length(l.old.s) != 0 && print(io, " ")
88+
printstyled(io, l.new.s, color = :light_green)
89+
else
90+
print(io, l.new)
91+
end
92+
end
93+
94+
struct SystemStructureDiffPrintMatrix <:
95+
AbstractMatrix{Union{LabelDiff, BipartiteAdjacencyListDiff}}
96+
new::SystemStructurePrintMatrix
97+
old::SystemStructurePrintMatrix
98+
end
99+
100+
function DeepDiffs.deepdiff(old::Union{MatchedSystemStructure, SystemStructure},
101+
new::Union{MatchedSystemStructure, SystemStructure})
102+
new_sspm = SystemStructurePrintMatrix(new)
103+
old_sspm = SystemStructurePrintMatrix(old)
104+
Base.print_matrix(stdout, SystemStructureDiffPrintMatrix(new_sspm, old_sspm))
105+
end
106+
107+
function Base.size(ssdpm::SystemStructureDiffPrintMatrix)
108+
max.(Base.size(ssdpm.new), Base.size(ssdpm.old))
109+
end
110+
111+
function Base.getindex(ssdpm::SystemStructureDiffPrintMatrix, i::Integer, j::Integer)
112+
checkbounds(ssdpm, i, j)
113+
if i > 1 && (j == 3 || j == 7)
114+
old = new = BipartiteAdjacencyList(nothing, nothing, unassigned)
115+
(i <= size(ssdpm.new, 1)) && (new = ssdpm.new[i, j])
116+
(i <= size(ssdpm.old, 1)) && (old = ssdpm.old[i, j])
117+
BipartiteAdjacencyListDiff(new, old)
118+
else
119+
old = new = Label("")
120+
(i <= size(ssdpm.new, 1)) && (new = ssdpm.new[i, j])
121+
(i <= size(ssdpm.old, 1)) && (old = ssdpm.old[i, j])
122+
LabelDiff(new, old)
123+
end
124+
end
125+
126+
end # module

src/ModelingToolkit.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ export @variables, @parameters, @constants, @brownian
221221
export @named, @nonamespace, @namespace, extend, compose, complete
222222
export debug_system
223223

224+
export show_with_compare
225+
224226
#export Continuous, Discrete, sampletime, input_timedomain, output_timedomain
225227
#export has_discrete_domain, has_continuous_domain
226228
#export is_discrete_domain, is_continuous_domain, is_hybrid_domain

src/bipartite_graph.jl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,12 +213,14 @@ end
213213
struct HighlightInt
214214
i::Int
215215
highlight::Symbol
216-
underline::Bool
216+
match::Bool
217217
end
218218
Base.typeinfo_implicit(::Type{HighlightInt}) = true
219219
function Base.show(io::IO, hi::HighlightInt)
220-
if hi.underline
221-
printstyled(io, hi.i, color = hi.highlight, underline = true)
220+
if hi.match
221+
printstyled(io, "(", color = hi.highlight)
222+
printstyled(io, hi.i, color = hi.highlight)
223+
printstyled(io, ")", color = hi.highlight)
222224
else
223225
printstyled(io, hi.i, color = hi.highlight)
224226
end
@@ -227,6 +229,8 @@ end
227229
function Base.show(io::IO, l::BipartiteAdjacencyList)
228230
if l.match === true
229231
printstyled(io, "")
232+
else
233+
printstyled(io, " ")
230234
end
231235
if l.u === nothing
232236
printstyled(io, '', color = :light_black)

src/systems/systemstructure.jl

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,13 @@ struct SystemStructurePrintMatrix <:
393393
eq_to_diff::DiffGraph
394394
var_eq_matching::Union{Matching, Nothing}
395395
end
396+
function SystemStructurePrintMatrix(s::SystemStructure)
397+
return SystemStructurePrintMatrix(complete(s.graph),
398+
complete(s.solvable_graph),
399+
complete(s.var_to_diff),
400+
complete(s.eq_to_diff),
401+
nothing)
402+
end
396403
Base.size(bgpm::SystemStructurePrintMatrix) = (max(nsrcs(bgpm.bpg), ndsts(bgpm.bpg)) + 1, 7)
397404
function compute_diff_label(diff_graph, i)
398405
di = i - 1 <= length(diff_graph) ? diff_graph[i - 1] : nothing
@@ -404,7 +411,7 @@ end
404411
function Base.getindex(bgpm::SystemStructurePrintMatrix, i::Integer, j::Integer)
405412
checkbounds(bgpm, i, j)
406413
if i <= 1
407-
return (Label.(("#", "∂ₜ", "eq", "", "#", "∂ₜ", "v")))[j]
414+
return (Label.(("#", "∂ₜ", " eq", "", "#", "∂ₜ", " v")))[j]
408415
elseif j == 4
409416
colors = Base.text_colors
410417
return Label("|", :light_black)
@@ -446,16 +453,12 @@ end
446453
function Base.show(io::IO, mime::MIME"text/plain", s::SystemStructure)
447454
@unpack graph, solvable_graph, var_to_diff, eq_to_diff = s
448455
if !get(io, :limit, true) || !get(io, :mtk_limit, true)
449-
print(io, "SystemStructure with ", length(graph.fadjlist), " equations and ",
450-
isa(graph.badjlist, Int) ? graph.badjlist : length(graph.badjlist),
456+
print(io, "SystemStructure with ", length(s.graph.fadjlist), " equations and ",
457+
isa(s.graph.badjlist, Int) ? s.graph.badjlist : length(s.graph.badjlist),
451458
" variables\n")
452-
Base.print_matrix(io,
453-
SystemStructurePrintMatrix(complete(graph),
454-
complete(solvable_graph),
455-
complete(var_to_diff),
456-
complete(eq_to_diff), nothing))
459+
Base.print_matrix(io, SystemStructurePrintMatrix(s))
457460
else
458-
S = incidence_matrix(graph, Num(Sym{Real}(:×)))
461+
S = incidence_matrix(s.graph, Num(Sym{Real}(:×)))
459462
print(io, "Incidence matrix:")
460463
show(io, mime, S)
461464
end
@@ -466,18 +469,34 @@ struct MatchedSystemStructure
466469
var_eq_matching::Matching
467470
end
468471

472+
function SystemStructurePrintMatrix(ms::MatchedSystemStructure)
473+
return SystemStructurePrintMatrix(complete(ms.structure.graph),
474+
complete(ms.structure.solvable_graph),
475+
complete(ms.structure.var_to_diff),
476+
complete(ms.structure.eq_to_diff),
477+
complete(ms.var_eq_matching,
478+
nsrcs(ms.structure.graph)))
479+
end
480+
481+
function Base.copy(ms::MatchedSystemStructure)
482+
MatchedSystemStructure(Base.copy(ms.structure), Base.copy(ms.var_eq_matching))
483+
end
484+
469485
function Base.show(io::IO, mime::MIME"text/plain", ms::MatchedSystemStructure)
470486
s = ms.structure
471487
@unpack graph, solvable_graph, var_to_diff, eq_to_diff = s
472488
print(io, "Matched SystemStructure with ", length(graph.fadjlist), " equations and ",
473489
isa(graph.badjlist, Int) ? graph.badjlist : length(graph.badjlist),
474490
" variables\n")
475-
Base.print_matrix(io,
476-
SystemStructurePrintMatrix(complete(graph),
477-
complete(solvable_graph),
478-
complete(var_to_diff),
479-
complete(eq_to_diff),
480-
complete(ms.var_eq_matching, nsrcs(graph))))
491+
Base.print_matrix(io, SystemStructurePrintMatrix(ms))
492+
printstyled(io, "\n\nLegend: ")
493+
printstyled(io, "Solvable")
494+
print(io, " | ")
495+
printstyled(io, "Unsolvable", color = :light_black)
496+
print(io, " | ")
497+
printstyled(io, "(Matched)")
498+
print(io, " | ")
499+
printstyled(io, " ∫ SelectedState ")
481500
end
482501

483502
# TODO: clean up

0 commit comments

Comments
 (0)