Skip to content

Commit d677739

Browse files
committed
Merge branch 'master' into id-tutorial
2 parents 2bcf83b + e803bac commit d677739

27 files changed

+292
-139
lines changed

Project.toml

Lines changed: 5 additions & 5 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 = "6.4.8"
4+
version = "6.6.0"
55

66
[deps]
77
AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
@@ -56,7 +56,7 @@ Distributions = "0.23, 0.24, 0.25"
5656
DocStringExtensions = "0.7, 0.8"
5757
DomainSets = "0.5"
5858
IfElse = "0.1"
59-
JuliaFormatter = "0.12, 0.13, 0.14, 0.15"
59+
JuliaFormatter = "0.12, 0.13, 0.14, 0.15, 0.16, 0.17"
6060
LabelledArrays = "1.3"
6161
Latexify = "0.11, 0.12, 0.13, 0.14, 0.15"
6262
LightGraphs = "1.3"
@@ -69,11 +69,11 @@ Requires = "1.0"
6969
RuntimeGeneratedFunctions = "0.4.3, 0.5"
7070
SafeTestsets = "0.0.1"
7171
SciMLBase = "1.3"
72-
Setfield = "0.7"
72+
Setfield = "0.7, 0.8"
7373
SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1.0"
7474
StaticArrays = "0.10, 0.11, 0.12, 1.0"
75-
SymbolicUtils = "0.13.4"
76-
Symbolics = "3.1"
75+
SymbolicUtils = "0.16"
76+
Symbolics = "3.3.0"
7777
UnPack = "0.1, 1.0"
7878
Unitful = "1.1"
7979
julia = "1.2"

src/systems/abstractsystem.jl

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ end
397397
function parameters(sys::AbstractSystem)
398398
ps = get_ps(sys)
399399
systems = get_systems(sys)
400-
isempty(systems) ? ps : [ps; reduce(vcat,namespace_parameters.(systems))]
400+
unique(isempty(systems) ? ps : [ps; reduce(vcat,namespace_parameters.(systems))])
401401
end
402402

403403
function controls(sys::AbstractSystem)
@@ -420,7 +420,13 @@ Base.@deprecate default_p(x) defaults(x) false
420420
function defaults(sys::AbstractSystem)
421421
systems = get_systems(sys)
422422
defs = get_defaults(sys)
423-
isempty(systems) ? defs : mapreduce(namespace_defaults, merge, systems; init=defs)
423+
# `mapfoldr` is really important!!! We should prefer the base model for
424+
# defaults, because people write:
425+
#
426+
# `compose(ODESystem(...; defaults=defs), ...)`
427+
#
428+
# Thus, right associativity is required and crucial for correctness.
429+
isempty(systems) ? defs : mapfoldr(namespace_defaults, merge, systems; init=defs)
424430
end
425431

426432
states(sys::AbstractSystem, v) = renamespace(sys, v)
@@ -760,7 +766,7 @@ end
760766
function _config(expr, namespace)
761767
cn = Base.Fix2(_config, namespace)
762768
if Meta.isexpr(expr, :.)
763-
return :($getvar($(map(cn, expr.args)...); namespace=$namespace))
769+
return :($getproperty($(map(cn, expr.args)...); namespace=$namespace))
764770
elseif Meta.isexpr(expr, :function)
765771
def = splitdef(expr)
766772
def[:args] = map(cn, def[:args])

src/systems/alias_elimination.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ using SymbolicUtils: Rewriters
33
const KEEP = typemin(Int)
44

55
function alias_elimination(sys)
6-
sys = initialize_system_structure(sys)
6+
sys = initialize_system_structure(sys; quick_cancel=true)
77
s = structure(sys)
88
is_linear_equations, eadj, cadj = find_linear_equations(sys)
99

src/systems/diffeqs/abstractodesystem.jl

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ function DiffEqBase.ODEFunction{iip}(sys::AbstractODESystem, dvs = states(sys),
261261

262262
obs = observed(sys)
263263
observedfun = if steady_state
264-
isempty(obs) ? SciMLBase.DEFAULT_OBSERVED_NO_TIME : let sys = sys, dict = Dict()
264+
let sys = sys, dict = Dict()
265265
function generated_observed(obsvar, u, p, t=Inf)
266266
obs = get!(dict, value(obsvar)) do
267267
build_explicit_observed_function(sys, obsvar)
@@ -270,7 +270,7 @@ function DiffEqBase.ODEFunction{iip}(sys::AbstractODESystem, dvs = states(sys),
270270
end
271271
end
272272
else
273-
isempty(obs) ? SciMLBase.DEFAULT_OBSERVED : let sys = sys, dict = Dict()
273+
let sys = sys, dict = Dict()
274274
function generated_observed(obsvar, u, p, t)
275275
obs = get!(dict, value(obsvar)) do
276276
build_explicit_observed_function(sys, obsvar; checkbounds=checkbounds)
@@ -338,16 +338,7 @@ function DiffEqBase.DAEFunction{iip}(sys::AbstractODESystem, dvs = states(sys),
338338
# TODO: Jacobian sparsity / sparse Jacobian / dense Jacobian
339339

340340
#=
341-
observedfun = let sys = sys, dict = Dict()
342341
# TODO: We don't have enought information to reconstruct arbitrary state
343-
# in general from `(u, p, t)`, e.g. `a ~ D(x)`.
344-
function generated_observed(obsvar, u, p, t)
345-
obs = get!(dict, value(obsvar)) do
346-
build_explicit_observed_function(sys, obsvar)
347-
end
348-
obs(u, p, t)
349-
end
350-
end
351342
=#
352343

353344
DAEFunction{iip}(
@@ -394,23 +385,6 @@ function ODEFunctionExpr{iip}(sys::AbstractODESystem, dvs = states(sys),
394385
f_oop, f_iip = generate_function(sys, dvs, ps; expression=Val{true}, kwargs...)
395386

396387
dict = Dict()
397-
#=
398-
observedfun = if steady_state
399-
:(function generated_observed(obsvar, u, p, t=Inf)
400-
obs = get!($dict, value(obsvar)) do
401-
build_explicit_observed_function($sys, obsvar)
402-
end
403-
obs(u, p, t)
404-
end)
405-
else
406-
:(function generated_observed(obsvar, u, p, t)
407-
obs = get!($dict, value(obsvar)) do
408-
build_explicit_observed_function($sys, obsvar)
409-
end
410-
obs(u, p, t)
411-
end)
412-
end
413-
=#
414388

415389
fsym = gensym(:f)
416390
_f = :($fsym = $ODEFunctionClosure($f_oop, $f_iip))

src/systems/diffeqs/modelingtoolkitize.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ function modelingtoolkitize(prob::DiffEqBase.ODEProblem; kwargs...)
5151

5252
sts = vec(collect(vars))
5353

54-
params = if params isa Array && ndims(params) == 0
54+
params = if params isa Number || (params isa Array && ndims(params) == 0)
5555
[params[1]]
5656
else
5757
vec(collect(params))

src/systems/diffeqs/odesystem.jl

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ eqs = [D(x) ~ σ*(y-x),
1919
D(y) ~ x*(ρ-z)-y,
2020
D(z) ~ x*y - β*z]
2121
22-
de = ODESystem(eqs,t,[x,y,z],[σ,ρ,β])
22+
@named de = ODESystem(eqs,t,[x,y,z],[σ,ρ,β])
2323
```
2424
"""
2525
struct ODESystem <: AbstractODESystem
@@ -225,42 +225,52 @@ Build the observed function assuming the observed equations are all explicit,
225225
i.e. there are no cycles.
226226
"""
227227
function build_explicit_observed_function(
228-
sys, syms;
228+
sys, ts;
229229
expression=false,
230230
output_type=Array,
231231
checkbounds=true)
232232

233-
if (isscalar = !(syms isa Vector))
234-
syms = [syms]
233+
if (isscalar = !(ts isa AbstractVector))
234+
ts = [ts]
235235
end
236-
syms = value.(syms)
236+
ts = Symbolics.scalarize.(value.(ts))
237+
238+
vars = Set()
239+
syms = foreach(Base.Fix1(vars!, vars), ts)
240+
ivs = independent_variables(sys)
241+
dep_vars = collect(setdiff(vars, ivs))
237242

238243
obs = observed(sys)
244+
sts = Set(states(sys))
239245
observed_idx = Dict(map(x->x.lhs, obs) .=> 1:length(obs))
240-
output = similar(syms, Any)
241-
# FIXME: this is a rather rough estimate of dependencies.
246+
247+
# FIXME: This is a rather rough estimate of dependencies. We assume
248+
# the expression depends on everything before the `maxidx`.
242249
maxidx = 0
243-
for (i, s) in enumerate(syms)
250+
for (i, s) in enumerate(dep_vars)
244251
idx = get(observed_idx, s, nothing)
245-
idx === nothing && throw(ArgumentError("$s is not an observed variable."))
252+
if idx === nothing
253+
if !(s in sts)
254+
throw(ArgumentError("$s is either an observed nor a state variable."))
255+
end
256+
continue
257+
end
246258
idx > maxidx && (maxidx = idx)
247-
output[i] = obs[idx].rhs
248259
end
260+
obsexprs = map(eq -> eq.lhseq.rhs, obs[1:maxidx])
249261

250262
dvs = DestructuredArgs(states(sys), inbounds=!checkbounds)
251263
ps = DestructuredArgs(parameters(sys), inbounds=!checkbounds)
252-
ivs = independent_variables(sys)
253264
args = [dvs, ps, ivs...]
254265
pre = get_postprocess_fbody(sys)
255266

256267
ex = Func(
257268
args, [],
258269
pre(Let(
259-
map(eq -> eq.lhseq.rhs, obs[1:maxidx]),
260-
isscalar ? output[1] : MakeArray(output, output_type)
270+
obsexprs,
271+
isscalar ? ts[1] : MakeArray(ts, output_type)
261272
))
262273
) |> toexpr
263-
264274
expression ? ex : @RuntimeGeneratedFunction(ex)
265275
end
266276

src/systems/diffeqs/sdesystem.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ noiseeqs = [0.1*x,
2323
0.1*y,
2424
0.1*z]
2525
26-
de = SDESystem(eqs,noiseeqs,t,[x,y,z],[σ,ρ,β])
26+
@named de = SDESystem(eqs,noiseeqs,t,[x,y,z],[σ,ρ,β])
2727
```
2828
"""
2929
struct SDESystem <: AbstractODESystem

src/systems/discrete_system/discrete_system.jl

Lines changed: 83 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@ $(FIELDS)
1111
```
1212
using ModelingToolkit
1313
14-
@parameters σ ρ β
15-
@variables t x(t) y(t) z(t) next_x(t) next_y(t) next_z(t)
14+
@parameters σ=28.0 ρ=10.0 β=8/3 δt=0.1
15+
@variables t x(t)=1.0 y(t)=0.0 z(t)=0.0
16+
D = Difference(t; dt=δt)
1617
17-
eqs = [next_x ~ σ*(y-x),
18-
next_y ~ x*(ρ-z)-y,
19-
next_z ~ x*y - β*z]
18+
eqs = [D(x) ~ σ*(y-x),
19+
D(y) ~ x*(ρ-z)-y,
20+
D(z) ~ x*y - β*z]
2021
21-
de = DiscreteSystem(eqs,t,[x,y,z],[σ,ρ,β])
22+
@named de = DiscreteSystem(eqs,t,[x,y,z],[σ,ρ,β]) # or
23+
@named de = DiscreteSystem(eqs)
2224
```
2325
"""
2426
struct DiscreteSystem <: AbstractTimeDependentSystem
@@ -45,22 +47,21 @@ struct DiscreteSystem <: AbstractTimeDependentSystem
4547
"""
4648
systems::Vector{DiscreteSystem}
4749
"""
48-
default_u0: The default initial conditions to use when initial conditions
49-
are not supplied in `DiscreteSystem`.
50+
defaults: The default values to use when initial conditions and/or
51+
parameters are not supplied in `DiscreteProblem`.
5052
"""
51-
default_u0::Dict
53+
defaults::Dict
5254
"""
53-
default_p: The default parameters to use when parameters are not supplied
54-
in `DiscreteSystem`.
55+
type: type of the system
5556
"""
56-
default_p::Dict
57-
function DiscreteSystem(discreteEqs, iv, dvs, ps, var_to_name, ctrls, observed, name, systems, default_u0, default_p; checks::Bool = true)
57+
connection_type::Any
58+
function DiscreteSystem(discreteEqs, iv, dvs, ps, var_to_name, ctrls, observed, name, systems, defaults, connection_type; checks::Bool = true)
5859
if checks
5960
check_variables(dvs, iv)
6061
check_parameters(ps, iv)
6162
all_dimensionless([dvs;ps;iv;ctrls]) ||check_units(discreteEqs)
6263
end
63-
new(discreteEqs, iv, dvs, ps, var_to_name, ctrls, observed, name, systems, default_u0, default_p)
64+
new(discreteEqs, iv, dvs, ps, var_to_name, ctrls, observed, name, systems, defaults, connection_type)
6465
end
6566
end
6667

@@ -78,6 +79,7 @@ function DiscreteSystem(
7879
default_u0=Dict(),
7980
default_p=Dict(),
8081
defaults=_merge(Dict(default_u0), Dict(default_p)),
82+
connection_type=nothing,
8183
kwargs...,
8284
)
8385
name === nothing && throw(ArgumentError("The `name` keyword must be provided. Please consider using the `@named` macro"))
@@ -88,7 +90,7 @@ function DiscreteSystem(
8890
ctrl′ = value.(controls)
8991

9092
if !(isempty(default_u0) && isempty(default_p))
91-
Base.depwarn("`default_u0` and `default_p` are deprecated. Use `defaults` instead.", :ODESystem, force=true)
93+
Base.depwarn("`default_u0` and `default_p` are deprecated. Use `defaults` instead.", :DiscreteSystem, force=true)
9294
end
9395
defaults = todict(defaults)
9496
defaults = Dict(value(k) => value(v) for (k, v) in pairs(defaults))
@@ -101,7 +103,46 @@ function DiscreteSystem(
101103
if length(unique(sysnames)) != length(sysnames)
102104
throw(ArgumentError("System names must be unique."))
103105
end
104-
DiscreteSystem(eqs, iv′, dvs′, ps′, var_to_name, ctrl′, observed, name, systems, default_u0, default_p, kwargs...)
106+
DiscreteSystem(eqs, iv′, dvs′, ps′, var_to_name, ctrl′, observed, name, systems, defaults, connection_type, kwargs...)
107+
end
108+
109+
110+
function DiscreteSystem(eqs, iv=nothing; kwargs...)
111+
eqs = collect(eqs)
112+
# NOTE: this assumes that the order of algebric equations doesn't matter
113+
diffvars = OrderedSet()
114+
allstates = OrderedSet()
115+
ps = OrderedSet()
116+
# reorder equations such that it is in the form of `diffeq, algeeq`
117+
diffeq = Equation[]
118+
algeeq = Equation[]
119+
# initial loop for finding `iv`
120+
if iv === nothing
121+
for eq in eqs
122+
if !(eq.lhs isa Number) # assume eq.lhs is either Differential or Number
123+
iv = iv_from_nested_difference(eq.lhs)
124+
break
125+
end
126+
end
127+
end
128+
iv = value(iv)
129+
iv === nothing && throw(ArgumentError("Please pass in independent variables."))
130+
for eq in eqs
131+
collect_vars_difference!(allstates, ps, eq.lhs, iv)
132+
collect_vars_difference!(allstates, ps, eq.rhs, iv)
133+
if isdifferenceeq(eq)
134+
diffvar, _ = var_from_nested_difference(eq.lhs)
135+
isequal(iv, iv_from_nested_difference(eq.lhs)) || throw(ArgumentError("A DiscreteSystem can only have one independent variable."))
136+
diffvar in diffvars && throw(ArgumentError("The difference variable $diffvar is not unique in the system of equations."))
137+
push!(diffvars, diffvar)
138+
push!(diffeq, eq)
139+
else
140+
push!(algeeq, eq)
141+
end
142+
end
143+
algevars = setdiff(allstates, diffvars)
144+
# the orders here are very important!
145+
return DiscreteSystem(append!(diffeq, algeeq), iv, vcat(collect(diffvars), collect(algevars)), ps; kwargs...)
105146
end
106147

107148
"""
@@ -118,16 +159,37 @@ function DiffEqBase.DiscreteProblem(sys::DiscreteSystem,u0map,tspan,
118159
ps = parameters(sys)
119160
eqs = equations(sys)
120161
eqs = linearize_eqs(sys, eqs)
121-
# defs = defaults(sys)
122-
t = get_iv(sys)
123-
u0 = varmap_to_vars(u0map,dvs)
162+
defs = defaults(sys)
163+
iv = get_iv(sys)
164+
165+
if parammap isa Dict
166+
u0defs = merge(parammap, defs)
167+
elseif eltype(parammap) <: Pair
168+
u0defs = merge(Dict(parammap), defs)
169+
elseif eltype(parammap) <: Number
170+
u0defs = merge(Dict(zip(ps, parammap)), defs)
171+
else
172+
u0defs = defs
173+
end
174+
if u0map isa Dict
175+
pdefs = merge(u0map, defs)
176+
elseif eltype(u0map) <: Pair
177+
pdefs = merge(Dict(u0map), defs)
178+
elseif eltype(u0map) <: Number
179+
pdefs = merge(Dict(zip(dvs, u0map)), defs)
180+
else
181+
pdefs = defs
182+
end
183+
184+
u0 = varmap_to_vars(u0map,dvs; defaults=u0defs)
185+
124186
rhss = [eq.rhs for eq in eqs]
125187
u = dvs
126-
p = varmap_to_vars(parammap,ps)
188+
p = varmap_to_vars(parammap,ps; defaults=pdefs)
127189

128190
f_gen = generate_function(sys; expression=Val{eval_expression}, expression_module=eval_module)
129191
f_oop, _ = (@RuntimeGeneratedFunction(eval_module, ex) for ex in f_gen)
130-
f(u,p,t) = f_oop(u,p,t)
192+
f(u,p,iv) = f_oop(u,p,iv)
131193
DiscreteProblem(f,u0,tspan,p;kwargs...)
132194
end
133195

0 commit comments

Comments
 (0)