Skip to content

Commit bf411e3

Browse files
authored
Merge branch 'SciML:master' into func-affect
2 parents 862ac7d + 15a7d07 commit bf411e3

24 files changed

+1382
-346
lines changed

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "ModelingToolkit"
22
uuid = "961ee093-0014-501f-94e3-6117800e7a78"
33
authors = ["Chris Rackauckas <[email protected]>"]
4-
version = "8.16.0"
4+
version = "8.17.0"
55

66
[deps]
77
AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
@@ -99,4 +99,4 @@ Sundials = "c3572dad-4567-51f8-b174-8c6c989267f4"
9999
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
100100

101101
[targets]
102-
test = ["AmplNLWriter", "BenchmarkTools", "ForwardDiff", "Ipopt", "Ipopt_jll", "ModelingToolkitStandardLibrary", "Optimization", "OptimizationOptimJL", "OptimizationMOI", "OrdinaryDiffEq", "Random", "ReferenceTests", "SafeTestsets", "Statistics", "SteadyStateDiffEq", "Test", "StochasticDiffEq", "Sundials"]
102+
test = ["AmplNLWriter", "BenchmarkTools", "ForwardDiff", "Ipopt", "Ipopt_jll", "ModelingToolkitStandardLibrary", "Optimization", "OptimizationOptimJL", "OptimizationMOI", "OrdinaryDiffEq", "Random", "ReferenceTests", "SafeTestsets", "Statistics", "SteadyStateDiffEq", "Test", "StochasticDiffEq", "Sundials"]

src/bipartite_graph.jl

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export BipartiteEdge, BipartiteGraph, DiCMOBiGraph, Unassigned, unassigned,
66

77
export 𝑠vertices, 𝑑vertices, has_𝑠vertex, has_𝑑vertex, 𝑠neighbors, 𝑑neighbors,
88
𝑠edges, 𝑑edges, nsrcs, ndsts, SRC, DST, set_neighbors!, invview,
9-
complete
9+
complete, delete_srcs!, delete_dsts!
1010

1111
using DocStringExtensions
1212
using UnPack
@@ -49,6 +49,10 @@ end
4949
function Matching(m::Int)
5050
Matching{Unassigned}(Union{Int, Unassigned}[unassigned for _ in 1:m], nothing)
5151
end
52+
function Matching{U}(m::Int) where {U}
53+
Matching{Union{Unassigned, U}}(Union{Int, Unassigned, U}[unassigned for _ in 1:m],
54+
nothing)
55+
end
5256

5357
Base.size(m::Matching) = Base.size(m.match)
5458
Base.getindex(m::Matching, i::Integer) = m.match[i]
@@ -65,9 +69,9 @@ function Base.setindex!(m::Matching{U}, v::Union{Integer, U}, i::Integer) where
6569
return m.match[i] = v
6670
end
6771

68-
function Base.push!(m::Matching{U}, v::Union{Integer, U}) where {U}
72+
function Base.push!(m::Matching, v)
6973
push!(m.match, v)
70-
if v !== unassigned && m.inv_match !== nothing
74+
if v isa Integer && m.inv_match !== nothing
7175
m.inv_match[v] = length(m.match)
7276
end
7377
end
@@ -346,8 +350,8 @@ vertices, subject to the constraint that vertices for which `srcfilter` or `dstf
346350
return `false` may not be matched.
347351
"""
348352
function maximal_matching(g::BipartiteGraph, srcfilter = vsrc -> true,
349-
dstfilter = vdst -> true)
350-
matching = Matching(ndsts(g))
353+
dstfilter = vdst -> true, ::Type{U} = Unassigned) where {U}
354+
matching = Matching{U}(ndsts(g))
351355
foreach(Iterators.filter(srcfilter, 𝑠vertices(g))) do vsrc
352356
construct_augmenting_path!(matching, g, vsrc, dstfilter)
353357
end
@@ -420,11 +424,15 @@ function Graphs.add_vertex!(g::BipartiteGraph{T}, type::VertType) where {T}
420424
return true # vertex successfully added
421425
end
422426

423-
function set_neighbors!(g::BipartiteGraph, i::Integer, new_neighbors::AbstractVector)
427+
function set_neighbors!(g::BipartiteGraph, i::Integer, new_neighbors)
424428
old_neighbors = g.fadjlist[i]
425429
old_nneighbors = length(old_neighbors)
426430
new_nneighbors = length(new_neighbors)
427-
g.fadjlist[i] = new_neighbors
431+
if iszero(new_nneighbors) # this handles Tuple as well
432+
empty!(g.fadjlist[i])
433+
else
434+
g.fadjlist[i] = new_neighbors
435+
end
428436
g.ne += new_nneighbors - old_nneighbors
429437
if isa(g.badjlist, AbstractVector)
430438
for n in old_neighbors
@@ -440,6 +448,14 @@ function set_neighbors!(g::BipartiteGraph, i::Integer, new_neighbors::AbstractVe
440448
end
441449
end
442450

451+
function delete_srcs!(g::BipartiteGraph, srcs)
452+
for s in srcs
453+
set_neighbors!(g, s, ())
454+
end
455+
g
456+
end
457+
delete_dsts!(g::BipartiteGraph, srcs) = delete_srcs!(invview(g), srcs)
458+
443459
###
444460
### Edges iteration
445461
###

src/inputoutput.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,7 @@ function generate_control_function(sys::AbstractODESystem, inputs = unbound_inpu
185185
error("No unbound inputs were found in system.")
186186
end
187187

188-
sys, diff_idxs, alge_idxs = io_preprocessing(sys, inputs, []; simplify,
189-
check_bound = false, kwargs...)
188+
sys, diff_idxs, alge_idxs = io_preprocessing(sys, inputs, []; simplify, kwargs...)
190189

191190
dvs = states(sys)
192191
ps = parameters(sys)

src/structural_transformation/bipartite_tearing/modia_tearing.jl

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,46 +9,90 @@ function try_assign_eq!(ict::IncrementalCycleTracker, vj::Integer, eq::Integer)
99
end
1010
end
1111

12-
function tearEquations!(ict::IncrementalCycleTracker, Gsolvable, es::Vector{Int},
13-
vs::Vector{Int})
12+
function try_assign_eq!(ict::IncrementalCycleTracker, vars, v_active, eq::Integer,
13+
condition::F = _ -> true) where {F}
1414
G = ict.graph
15-
vActive = BitSet(vs)
15+
for vj in vars
16+
(vj in v_active && G.matching[vj] === unassigned && condition(vj)) || continue
17+
try_assign_eq!(ict, vj, eq) && return true
18+
end
19+
return false
20+
end
1621

22+
function tearEquations!(ict::IncrementalCycleTracker, Gsolvable, es::Vector{Int},
23+
v_active::BitSet, isder′::F) where {F}
24+
check_der = isder′ !== nothing
25+
if check_der
26+
has_der = Ref(false)
27+
isder = let has_der = has_der, isder′ = isder′
28+
v -> begin
29+
r = isder′(v)
30+
has_der[] |= r
31+
r
32+
end
33+
end
34+
end
1735
for eq in es # iterate only over equations that are not in eSolvedFixed
18-
for vj in Gsolvable[eq]
19-
if G.matching[vj] === unassigned && (vj in vActive)
20-
r = try_assign_eq!(ict, vj, eq)
21-
r && break
36+
vs = Gsolvable[eq]
37+
if check_der
38+
# if there're differentiated variables, then only consider them
39+
try_assign_eq!(ict, vs, v_active, eq, isder)
40+
if has_der[]
41+
has_der[] = false
42+
continue
2243
end
2344
end
45+
try_assign_eq!(ict, vs, v_active, eq)
2446
end
2547

2648
return ict
2749
end
2850

29-
function tear_graph_block_modia!(var_eq_matching, graph, solvable_graph, eqs, vars)
51+
function tear_graph_block_modia!(var_eq_matching, graph, solvable_graph, eqs, vars,
52+
isder::F) where {F}
3053
ict = IncrementalCycleTracker(DiCMOBiGraph{true}(graph); dir = :in)
31-
tearEquations!(ict, solvable_graph.fadjlist, eqs, vars)
54+
tearEquations!(ict, solvable_graph.fadjlist, eqs, vars, isder)
3255
for var in vars
3356
var_eq_matching[var] = ict.graph.matching[var]
3457
end
3558
return nothing
3659
end
3760

38-
function tear_graph_modia(structure::SystemStructure; varfilter = v -> true,
39-
eqfilter = eq -> true)
61+
function tear_graph_modia(structure::SystemStructure, isder::F = nothing,
62+
::Type{U} = Unassigned;
63+
varfilter::F2 = v -> true,
64+
eqfilter::F3 = eq -> true) where {F, U, F2, F3}
65+
# It would be possible here to simply iterate over all variables and attempt to
66+
# use tearEquations! to produce a matching that greedily selects the minimal
67+
# number of torn variables. However, we can do this process faster if we first
68+
# compute the strongly connected components. In the absence of cycles and
69+
# non-solvability, a maximal matching on the original graph will give us an
70+
# optimal assignment. However, even with cycles, we can use the maximal matching
71+
# to give us a good starting point for a good matching and then proceed to
72+
# reverse edges in each scc to improve the solution. Note that it is possible
73+
# to have optimal solutions that cannot be found by this process. We will not
74+
# find them here [TODO: It would be good to have an explicit example of this.]
75+
4076
@unpack graph, solvable_graph = structure
41-
var_eq_matching = complete(maximal_matching(graph, eqfilter, varfilter))
77+
var_eq_matching = complete(maximal_matching(graph, eqfilter, varfilter, U))
4278
var_sccs::Vector{Union{Vector{Int}, Int}} = find_var_sccs(graph, var_eq_matching)
4379

80+
ieqs = Int[]
81+
filtered_vars = BitSet()
4482
for vars in var_sccs
45-
filtered_vars = filter(varfilter, vars)
46-
ieqs = Int[var_eq_matching[v]
47-
for v in filtered_vars if var_eq_matching[v] !== unassigned]
4883
for var in vars
84+
if varfilter(var)
85+
push!(filtered_vars, var)
86+
if var_eq_matching[var] !== unassigned
87+
push!(ieqs, var_eq_matching[var])
88+
end
89+
end
4990
var_eq_matching[var] = unassigned
5091
end
51-
tear_graph_block_modia!(var_eq_matching, graph, solvable_graph, ieqs, filtered_vars)
92+
tear_graph_block_modia!(var_eq_matching, graph, solvable_graph, ieqs, filtered_vars,
93+
isder)
94+
empty!(ieqs)
95+
empty!(filtered_vars)
5296
end
5397

5498
return var_eq_matching

src/structural_transformation/codegen.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ const MAX_INLINE_NLSOLVE_SIZE = 8
66

77
function torn_system_with_nlsolve_jacobian_sparsity(state, var_eq_matching, var_sccs,
88
nlsolve_scc_idxs, eqs_idxs, states_idxs)
9-
fullvars = state.fullvars
109
graph = state.structure.graph
1110

1211
# The sparsity pattern of `nlsolve(f, u, p)` w.r.t `p` is difficult to
@@ -71,7 +70,6 @@ function torn_system_with_nlsolve_jacobian_sparsity(state, var_eq_matching, var_
7170

7271
var2idx = Dict{Int, Int}(v => i for (i, v) in enumerate(states_idxs))
7372
eqs2idx = Dict{Int, Int}(v => i for (i, v) in enumerate(eqs_idxs))
74-
nlsolve_vars_set = BitSet(nlsolve_vars)
7573

7674
I = Int[]
7775
J = Int[]

src/structural_transformation/partial_state_selection.jl

Lines changed: 56 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ function pss_graph_modia!(structure::SystemStructure, var_eq_matching, varlevel,
8484
end
8585
filter!(var -> ict.graph.matching[var] === unassigned, to_tear_vars)
8686
filter!(eq -> invview(ict.graph.matching)[eq] === unassigned, to_tear_eqs)
87-
tearEquations!(ict, solvable_graph.fadjlist, to_tear_eqs, to_tear_vars)
87+
tearEquations!(ict, solvable_graph.fadjlist, to_tear_eqs, BitSet(to_tear_vars),
88+
nothing)
8889
for var in to_tear_vars
8990
var_eq_matching[var] = ict.graph.matching[var]
9091
end
@@ -146,57 +147,51 @@ function partial_state_selection_graph!(structure::SystemStructure, var_eq_match
146147
var_eq_matching
147148
end
148149

149-
function dummy_derivative_graph!(state::TransformationState, jac = nothing)
150+
function dummy_derivative_graph!(state::TransformationState, jac = nothing; kwargs...)
151+
state.structure.solvable_graph === nothing && find_solvables!(state; kwargs...)
150152
var_eq_matching = complete(pantelides!(state))
151153
complete!(state.structure)
152154
dummy_derivative_graph!(state.structure, var_eq_matching, jac)
153155
end
154156

155-
function dummy_derivative_graph!(structure::SystemStructure, var_eq_matching, jac)
156-
@unpack eq_to_diff, var_to_diff, graph = structure
157-
diff_to_eq = invview(eq_to_diff)
158-
diff_to_var = invview(var_to_diff)
159-
invgraph = invview(graph)
160-
161-
neqs = nsrcs(graph)
162-
eqlevel = zeros(Int, neqs)
157+
function compute_diff_level(diff_to_x)
158+
nxs = length(diff_to_x)
159+
xlevel = zeros(Int, nxs)
163160
maxlevel = 0
164-
for i in 1:neqs
161+
for i in 1:nxs
165162
level = 0
166-
eq = i
167-
while diff_to_eq[eq] !== nothing
168-
eq = diff_to_eq[eq]
163+
x = i
164+
while diff_to_x[x] !== nothing
165+
x = diff_to_x[x]
169166
level += 1
170167
end
171168
maxlevel = max(maxlevel, level)
172-
eqlevel[i] = level
169+
xlevel[i] = level
173170
end
171+
return xlevel, maxlevel
172+
end
174173

175-
nvars = ndsts(graph)
176-
varlevel = zeros(Int, nvars)
177-
for i in 1:nvars
178-
level = 0
179-
var = i
180-
while diff_to_var[var] !== nothing
181-
var = diff_to_var[var]
182-
level += 1
183-
end
184-
maxlevel = max(maxlevel, level)
185-
varlevel[i] = level
186-
end
174+
function dummy_derivative_graph!(structure::SystemStructure, var_eq_matching, jac)
175+
@unpack eq_to_diff, var_to_diff, graph = structure
176+
diff_to_eq = invview(eq_to_diff)
177+
diff_to_var = invview(var_to_diff)
178+
invgraph = invview(graph)
179+
180+
eqlevel, _ = compute_diff_level(diff_to_eq)
187181

188182
var_sccs = find_var_sccs(graph, var_eq_matching)
189-
eqcolor = falses(neqs)
183+
eqcolor = falses(nsrcs(graph))
190184
dummy_derivatives = Int[]
191185
col_order = Int[]
186+
nvars = ndsts(graph)
192187
for vars in var_sccs
193188
eqs = [var_eq_matching[var] for var in vars if var_eq_matching[var] !== unassigned]
194189
isempty(eqs) && continue
195-
maxlevel = maximum(map(x -> eqlevel[x], eqs))
190+
maxlevel = maximum(Base.Fix1(getindex, eqlevel), eqs)
196191
iszero(maxlevel) && continue
197192

198193
rank_matching = Matching(nvars)
199-
for level in maxlevel:-1:1
194+
for _ in maxlevel:-1:1
200195
eqs = filter(eq -> diff_to_eq[eq] !== nothing, eqs)
201196
nrows = length(eqs)
202197
iszero(nrows) && break
@@ -220,8 +215,10 @@ function dummy_derivative_graph!(structure::SystemStructure, var_eq_matching, ja
220215
else
221216
rank = 0
222217
for var in vars
218+
# We need `invgraph` here because we are matching from
219+
# variables to equations.
223220
pathfound = construct_augmenting_path!(rank_matching, invgraph, var,
224-
eq -> eq in eqs_set, eqcolor)
221+
Base.Fix2(in, eqs_set), eqcolor)
225222
pathfound || continue
226223
push!(dummy_derivatives, var)
227224
rank += 1
@@ -239,5 +236,32 @@ function dummy_derivative_graph!(structure::SystemStructure, var_eq_matching, ja
239236
end
240237
end
241238

242-
dummy_derivatives
239+
dummy_derivatives_set = BitSet(dummy_derivatives)
240+
# We can eliminate variables that are not a selected state (differential
241+
# variables). Selected states are differentiated variables that are not
242+
# dummy derivatives.
243+
can_eliminate = let var_to_diff = var_to_diff,
244+
dummy_derivatives_set = dummy_derivatives_set
245+
246+
v -> begin
247+
dv = var_to_diff[v]
248+
dv === nothing || dv in dummy_derivatives_set
249+
end
250+
end
251+
252+
# We don't want tearing to give us `y_t ~ D(y)`, so we skip equations with
253+
# actually differentiated variables.
254+
isdiffed = let diff_to_var = diff_to_var, dummy_derivatives_set = dummy_derivatives_set
255+
v -> diff_to_var[v] !== nothing && !(v in dummy_derivatives_set)
256+
end
257+
258+
var_eq_matching = tear_graph_modia(structure, isdiffed,
259+
Union{Unassigned, SelectedState};
260+
varfilter = can_eliminate)
261+
for v in eachindex(var_eq_matching)
262+
can_eliminate(v) && continue
263+
var_eq_matching[v] = SelectedState()
264+
end
265+
266+
return var_eq_matching
243267
end

0 commit comments

Comments
 (0)