Skip to content

Commit e639a9f

Browse files
committed
Fix pantelides-with-ag corner case
Fixes the corner case mentioned in the doc comment. In partiuclar, when we have an incomplete alias chain, the alias can change which variable needs to be considered the highest-differentiated. While we're at it, also take care of the todo in the same place.
1 parent d8eb9e2 commit e639a9f

File tree

1 file changed

+81
-21
lines changed

1 file changed

+81
-21
lines changed

src/structural_transformation/pantelides.jl

Lines changed: 81 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,86 @@ function pantelides_reassemble(state::TearingState, var_eq_matching)
6969
return sys
7070
end
7171

72+
"""
73+
computed_highest_diff_variables(var_to_diff, ag)
74+
75+
Computes which variables are the "highest-differentiated" for purposes of
76+
pantelides. Ordinarily this is relatively straightforward. However, in our
77+
case, there are two complicating conditions:
78+
79+
1. We allow variables in the structure graph that don't appear in the
80+
system at all. What we are interested in is the highest-differentiated
81+
variable that actually appears in the system.
82+
83+
2. We have an alias graph. The alias graph implicitly contributes an
84+
alias equation, so it doesn't actually whitelist any additional variables,
85+
but it may change which variable is considered the highest differentiated one.
86+
Consider the following situation:
87+
88+
Vars: x, y
89+
Eqs: 0 = f(x)
90+
Alias: ẋ = ẏ
91+
92+
In the absence of the alias, we would consider `x` to be the highest
93+
differentiated variable. However, because of the alias (and because there
94+
is no alias for `x=y`), we actually need to take `ẋ` as the highest
95+
differentiated variable.
96+
97+
This function takes care of these complications are returns a boolean array
98+
for every variable, indicating whether it is considered "highest-differentiated".
99+
"""
100+
function computed_highest_diff_variables(structure, ag::Union{AliasGraph, Nothing})
101+
@unpack graph, var_to_diff = structure
102+
103+
nvars = length(var_to_diff)
104+
varwhitelist = falses(nvars)
105+
for var in 1:nvars
106+
if var_to_diff[var] === nothing && !varwhitelist[var]
107+
while isempty(𝑑neighbors(graph, var)) && (ag === nothing || !haskey(ag, var))
108+
var′ = invview(var_to_diff)[var]
109+
var′ === nothing && break
110+
var = var′
111+
end
112+
if ag !== nothing && haskey(ag, var)
113+
(_, stem) = ag[var]
114+
stem == 0 && continue
115+
# Ascend the stem
116+
while isempty(𝑑neighbors(graph, var))
117+
var′ = invview(var_to_diff)[var]
118+
var′ === nothing && break
119+
stem′ = invview(var_to_diff)[var]
120+
# Invariant from alias elimination: Stem is chosen to have
121+
# the highest differentiation order.
122+
@assert stem′ !== nothing
123+
if !haskey(ag, var′) || ag[var′][2] != stem′
124+
varwhitelist[stem] = true
125+
break
126+
end
127+
stem = stem′
128+
var = var′
129+
end
130+
else
131+
varwhitelist[var] = true
132+
end
133+
end
134+
end
135+
136+
# Remove any variables from the varwhitelist for whom a higher-differentiated
137+
# var is already on the whitelist
138+
for var in 1:nvars
139+
varwhitelist[var] || continue
140+
var′ = var
141+
while (var′ = var_to_diff[var′]) !== nothing
142+
if varwhitelist[var′]
143+
varwhitelist[var] = false
144+
break
145+
end
146+
end
147+
end
148+
149+
return varwhitelist
150+
end
151+
72152
"""
73153
pantelides!(state::TransformationState; kwargs...)
74154
@@ -86,27 +166,7 @@ function pantelides!(state::TransformationState, ag::Union{AliasGraph, Nothing}
86166
nnonemptyeqs = count(eq -> !isempty(𝑠neighbors(graph, eq)) && eq_to_diff[eq] === nothing,
87167
1:neqs′)
88168

89-
# Allow matching for the highest differentiated variable that
90-
# currently appears in an equation (or implicit equation in a side ag)
91-
varwhitelist = falses(nvars)
92-
for var in 1:nvars
93-
if var_to_diff[var] === nothing && !varwhitelist[var]
94-
while isempty(𝑑neighbors(graph, var)) && (ag === nothing || !haskey(ag, var))
95-
var′ = invview(var_to_diff)[var]
96-
var′ === nothing && break
97-
var = var′
98-
end
99-
if !isempty(𝑑neighbors(graph, var))
100-
if ag !== nothing && haskey(ag, var)
101-
# TODO: remove lower diff vars from whitelist
102-
c, a = ag[var]
103-
iszero(c) || (varwhitelist[a] = true)
104-
else
105-
varwhitelist[var] = true
106-
end
107-
end
108-
end
109-
end
169+
varwhitelist = computed_highest_diff_variables(state.structure, ag)
110170

111171
if nnonemptyeqs > count(varwhitelist)
112172
throw(InvalidSystemException("System is structurally singular"))

0 commit comments

Comments
 (0)