Skip to content

Commit d4e0d0f

Browse files
feat: enable structural_simplify to automatically handle non-fully-determined systems
Uses the result of `check_consistency`
1 parent 14a7239 commit d4e0d0f

File tree

2 files changed

+49
-18
lines changed

2 files changed

+49
-18
lines changed

src/structural_transformation/utils.jl

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,41 @@ end
5858
###
5959
### Structural check
6060
###
61-
function check_consistency(state::TransformationState, orig_inputs)
61+
62+
"""
63+
$(TYPEDSIGNATURES)
64+
65+
Check if the `state` represents a singular system, and return the unmatched variables.
66+
"""
67+
function singular_check(state::TransformationState)
68+
@unpack graph, var_to_diff = state.structure
69+
fullvars = get_fullvars(state)
70+
# This is defined to check if Pantelides algorithm terminates. For more
71+
# details, check the equation (15) of the original paper.
72+
extended_graph = (@set graph.fadjlist = Vector{Int}[graph.fadjlist;
73+
map(collect, edges(var_to_diff))])
74+
extended_var_eq_matching = maximal_matching(extended_graph)
75+
76+
nvars = ndsts(graph)
77+
unassigned_var = []
78+
for (vj, eq) in enumerate(extended_var_eq_matching)
79+
vj > nvars && break
80+
if eq === unassigned && !isempty(𝑑neighbors(graph, vj))
81+
push!(unassigned_var, fullvars[vj])
82+
end
83+
end
84+
return unassigned_var
85+
end
86+
87+
"""
88+
$(TYPEDSIGNATURES)
89+
90+
Check the consistency of `state`, given the inputs `orig_inputs`. If `nothrow == false`,
91+
throws an error if the system is under-/over-determined or singular. In this case, if the
92+
function returns it will return `true`. If `nothrow == true`, it will return `false`
93+
instead of throwing an error. The singular case will print a warning.
94+
"""
95+
function check_consistency(state::TransformationState, orig_inputs; nothrow = false)
6296
fullvars = get_fullvars(state)
6397
neqs = n_concrete_eqs(state)
6498
@unpack graph, var_to_diff = state.structure
@@ -72,6 +106,7 @@ function check_consistency(state::TransformationState, orig_inputs)
72106
is_balanced = n_highest_vars == neqs
73107

74108
if neqs > 0 && !is_balanced
109+
nothrow && return false
75110
varwhitelist = var_to_diff .== nothing
76111
var_eq_matching = maximal_matching(graph, eq -> true, v -> varwhitelist[v]) # not assigned
77112
# Just use `error_reporting` to do conditional
@@ -85,20 +120,7 @@ function check_consistency(state::TransformationState, orig_inputs)
85120
error_reporting(state, bad_idxs, n_highest_vars, iseqs, orig_inputs)
86121
end
87122

88-
# This is defined to check if Pantelides algorithm terminates. For more
89-
# details, check the equation (15) of the original paper.
90-
extended_graph = (@set graph.fadjlist = Vector{Int}[graph.fadjlist;
91-
map(collect, edges(var_to_diff))])
92-
extended_var_eq_matching = maximal_matching(extended_graph)
93-
94-
nvars = ndsts(graph)
95-
unassigned_var = []
96-
for (vj, eq) in enumerate(extended_var_eq_matching)
97-
vj > nvars && break
98-
if eq === unassigned && !isempty(𝑑neighbors(graph, vj))
99-
push!(unassigned_var, fullvars[vj])
100-
end
101-
end
123+
unassigned_var = singular_check(state)
102124

103125
if !isempty(unassigned_var) || !is_balanced
104126
io = IOBuffer()
@@ -107,10 +129,14 @@ function check_consistency(state::TransformationState, orig_inputs)
107129
errmsg = "The system is structurally singular! " *
108130
"Here are the problematic variables: \n" *
109131
unassigned_var_str
132+
if nothrow
133+
@warn errmsg
134+
return false
135+
end
110136
throw(InvalidSystemException(errmsg))
111137
end
112138

113-
return nothing
139+
return true
114140
end
115141

116142
###

src/systems/systemstructure.jl

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -677,7 +677,11 @@ function _structural_simplify!(state::TearingState, io; simplify = false,
677677
check_consistency = true, fully_determined = true, warn_initialize_determined = false,
678678
dummy_derivative = true,
679679
kwargs...)
680-
check_consistency &= fully_determined
680+
if fully_determined isa Bool
681+
check_consistency &= fully_determined
682+
else
683+
check_consistency = true
684+
end
681685
has_io = io !== nothing
682686
orig_inputs = Set()
683687
if has_io
@@ -690,7 +694,8 @@ function _structural_simplify!(state::TearingState, io; simplify = false,
690694
end
691695
sys, mm = ModelingToolkit.alias_elimination!(state; kwargs...)
692696
if check_consistency
693-
ModelingToolkit.check_consistency(state, orig_inputs)
697+
fully_determined = ModelingToolkit.check_consistency(
698+
state, orig_inputs; nothrow = fully_determined === nothing)
694699
end
695700
if fully_determined && dummy_derivative
696701
sys = ModelingToolkit.dummy_derivative(

0 commit comments

Comments
 (0)