Skip to content

Commit 8db7590

Browse files
committed
Merge remote-tracking branch 'origin' into implicit_discrete_system
merge test fixes
2 parents f577084 + 57c79e9 commit 8db7590

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1985
-1459
lines changed

.github/workflows/Downstream.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ jobs:
3737
- {user: SciML, repo: MethodOfLines.jl, group: Interface}
3838
- {user: SciML, repo: MethodOfLines.jl, group: 2D_Diffusion}
3939
- {user: SciML, repo: MethodOfLines.jl, group: DAE}
40+
- {user: SciML, repo: ModelingToolkitNeuralNets.jl, group: All}
4041
steps:
4142
- uses: actions/checkout@v4
4243
- uses: julia-actions/setup-julia@v1

Project.toml

Lines changed: 9 additions & 7 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 = ["Yingbo Ma <[email protected]>", "Chris Rackauckas <[email protected]> and contributors"]
4-
version = "9.62.0"
4+
version = "9.64.1"
55

66
[deps]
77
AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
@@ -104,11 +104,11 @@ DynamicQuantities = "^0.11.2, 0.12, 0.13, 1"
104104
EnumX = "1.0.4"
105105
ExprTools = "0.1.10"
106106
Expronicon = "0.8"
107+
FMI = "0.14"
107108
FindFirstFunctions = "1"
108109
ForwardDiff = "0.10.3"
109110
FunctionWrappers = "1.1"
110111
FunctionWrappersWrappers = "0.1"
111-
FMI = "0.14"
112112
Graphs = "1.5.2"
113113
HomotopyContinuation = "2.11"
114114
InfiniteOpt = "0.5"
@@ -119,6 +119,7 @@ LabelledArrays = "1.3"
119119
Latexify = "0.11, 0.12, 0.13, 0.14, 0.15, 0.16"
120120
Libdl = "1"
121121
LinearAlgebra = "1"
122+
Logging = "1"
122123
MLStyle = "0.4.17"
123124
ModelingToolkitStandardLibrary = "2.19"
124125
NaNMath = "0.3, 1"
@@ -128,26 +129,26 @@ OrderedCollections = "1"
128129
OrdinaryDiffEq = "6.82.0"
129130
OrdinaryDiffEqCore = "1.15.0"
130131
OrdinaryDiffEqDefault = "1.2"
131-
OrdinaryDiffEqNonlinearSolve = "1.3.0"
132+
OrdinaryDiffEqNonlinearSolve = "1.5.0"
132133
PrecompileTools = "1"
133134
REPL = "1"
134135
RecursiveArrayTools = "3.26"
135136
Reexport = "0.2, 1"
136137
RuntimeGeneratedFunctions = "0.5.9"
137138
SCCNonlinearSolve = "1.0.0"
138-
SciMLBase = "2.72.1"
139+
SciMLBase = "2.73"
139140
SciMLStructures = "1.0"
140141
Serialization = "1"
141142
Setfield = "0.7, 0.8, 1"
142143
SimpleNonlinearSolve = "0.1.0, 1, 2"
143144
SparseArrays = "1"
144145
SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1.0, 2"
145146
StaticArrays = "0.10, 0.11, 0.12, 1.0"
146-
StochasticDiffEq = "6.72.1"
147147
StochasticDelayDiffEq = "1.8.1"
148+
StochasticDiffEq = "6.72.1"
148149
SymbolicIndexingInterface = "0.3.37"
149150
SymbolicUtils = "3.14"
150-
Symbolics = "6.29"
151+
Symbolics = "6.29.1"
151152
URIs = "1"
152153
UnPack = "0.1, 1.0"
153154
Unitful = "1.1"
@@ -164,6 +165,7 @@ ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
164165
Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9"
165166
Ipopt_jll = "9cc047cb-c261-5740-88fc-0cf96f7bdcc7"
166167
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
168+
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
167169
ModelingToolkitStandardLibrary = "16a59e39-deab-5bd0-87e4-056b12336739"
168170
NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec"
169171
Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba"
@@ -187,4 +189,4 @@ Sundials = "c3572dad-4567-51f8-b174-8c6c989267f4"
187189
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
188190

189191
[targets]
190-
test = ["AmplNLWriter", "BenchmarkTools", "ControlSystemsBase", "DataInterpolations", "DelayDiffEq", "NonlinearSolve", "ForwardDiff", "Ipopt", "Ipopt_jll", "ModelingToolkitStandardLibrary", "Optimization", "OptimizationOptimJL", "OptimizationMOI", "OrdinaryDiffEq", "OrdinaryDiffEqCore", "OrdinaryDiffEqDefault", "REPL", "Random", "ReferenceTests", "SafeTestsets", "StableRNGs", "Statistics", "SteadyStateDiffEq", "Test", "StochasticDiffEq", "Sundials", "StochasticDelayDiffEq", "Pkg", "JET", "OrdinaryDiffEqNonlinearSolve"]
192+
test = ["AmplNLWriter", "BenchmarkTools", "ControlSystemsBase", "DataInterpolations", "DelayDiffEq", "NonlinearSolve", "ForwardDiff", "Ipopt", "Ipopt_jll", "ModelingToolkitStandardLibrary", "Optimization", "OptimizationOptimJL", "OptimizationMOI", "OrdinaryDiffEq", "OrdinaryDiffEqCore", "OrdinaryDiffEqDefault", "REPL", "Random", "ReferenceTests", "SafeTestsets", "StableRNGs", "Statistics", "SteadyStateDiffEq", "Test", "StochasticDiffEq", "Sundials", "StochasticDelayDiffEq", "Pkg", "JET", "OrdinaryDiffEqNonlinearSolve", "Logging"]

docs/Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba"
1515
OptimizationOptimJL = "36348300-93cb-4f02-beb5-3c3902f8871e"
1616
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
1717
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
18+
PreallocationTools = "d236fae5-4411-538c-8e31-a6e3d9e00b46"
1819
SciMLStructures = "53ae85a6-f571-4167-b2af-e1d143709226"
1920
Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46"
2021
StochasticDiffEq = "789caeaf-c7a9-5a7d-9973-96adeb23e2a0"
@@ -38,6 +39,7 @@ Optimization = "3.9, 4"
3839
OptimizationOptimJL = "0.1, 0.4"
3940
OrdinaryDiffEq = "6.31"
4041
Plots = "1.36"
42+
PreallocationTools = "0.4"
4143
SciMLStructures = "1.1"
4244
Setfield = "1"
4345
StochasticDiffEq = "6"

docs/src/basics/Variable_metadata.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ state_priority(important_dof)
201201
Units for variables can be designated using symbolic metadata. For more information, please see the [model validation and units](@ref units) section of the docs. Note that `getunit` is not equivalent to `get_unit` - the former is a metadata getter for individual variables (and is provided so the same interface function for `unit` exists like other metadata), while the latter is used to handle more general symbolic expressions.
202202

203203
```@example metadata
204+
using DynamicQuantities
204205
@variables speed [unit = u"m/s"]
205206
hasunit(speed)
206207
```

docs/src/examples/remake.md

Lines changed: 19 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,22 @@ parameters to optimize.
4545

4646
```@example Remake
4747
using SymbolicIndexingInterface: parameter_values, state_values
48-
using SciMLStructures: Tunable, replace, replace!
48+
using SciMLStructures: Tunable, canonicalize, replace, replace!
49+
using PreallocationTools
4950
5051
function loss(x, p)
5152
odeprob = p[1] # ODEProblem stored as parameters to avoid using global variables
5253
ps = parameter_values(odeprob) # obtain the parameter object from the problem
53-
ps = replace(Tunable(), ps, x) # create a copy with the values passed to the loss function
54+
diffcache = p[5]
55+
# get an appropriately typed preallocated buffer to store the `x` values in
56+
buffer = get_tmp(diffcache, x)
57+
# copy the current values to this buffer
58+
copyto!(buffer, canonicalize(Tunable(), ps)[1])
59+
# create a copy of the parameter object with the buffer
60+
ps = replace(Tunable(), ps, buffer)
61+
# set the updated values in the parameter object
62+
setter = p[4]
63+
setter(ps, x)
5464
# remake the problem, passing in our new parameter object
5565
newprob = remake(odeprob; p = ps)
5666
timesteps = p[2]
@@ -81,49 +91,22 @@ We can perform the optimization as below:
8191
```@example Remake
8292
using Optimization
8393
using OptimizationOptimJL
94+
using SymbolicIndexingInterface
8495
8596
# manually create an OptimizationFunction to ensure usage of `ForwardDiff`, which will
8697
# require changing the types of parameters from `Float64` to `ForwardDiff.Dual`
8798
optfn = OptimizationFunction(loss, Optimization.AutoForwardDiff())
99+
# function to set the parameters we are optimizing
100+
setter = setp(odeprob, [α, β, γ, δ])
101+
# `DiffCache` to avoid allocations
102+
diffcache = DiffCache(canonicalize(Tunable(), parameter_values(odeprob))[1])
88103
# parameter object is a tuple, to store differently typed objects together
89104
optprob = OptimizationProblem(
90-
optfn, rand(4), (odeprob, timesteps, data), lb = 0.1zeros(4), ub = 3ones(4))
105+
optfn, rand(4), (odeprob, timesteps, data, setter, diffcache),
106+
lb = 0.1zeros(4), ub = 3ones(4))
91107
sol = solve(optprob, BFGS())
92108
```
93109

94-
To identify which values correspond to which parameters, we can `replace!` them into the
95-
`ODEProblem`:
96-
97-
```@example Remake
98-
replace!(Tunable(), parameter_values(odeprob), sol.u)
99-
odeprob.ps[[α, β, γ, δ]]
100-
```
101-
102-
`replace!` operates in-place, so the values being replaced must be of the same type as those
103-
stored in the parameter object, or convertible to that type. For demonstration purposes, we
104-
can construct a loss function that uses `replace!`, and calculate gradients using
105-
`AutoFiniteDiff` rather than `AutoForwardDiff`.
106-
107-
```@example Remake
108-
function loss2(x, p)
109-
odeprob = p[1] # ODEProblem stored as parameters to avoid using global variables
110-
newprob = remake(odeprob) # copy the problem with `remake`
111-
# update the parameter values in-place
112-
replace!(Tunable(), parameter_values(newprob), x)
113-
timesteps = p[2]
114-
sol = solve(newprob, AutoTsit5(Rosenbrock23()); saveat = timesteps)
115-
truth = p[3]
116-
data = Array(sol)
117-
return sum((truth .- data) .^ 2) / length(truth)
118-
end
119-
120-
# use finite-differencing to calculate derivatives
121-
optfn2 = OptimizationFunction(loss2, Optimization.AutoFiniteDiff())
122-
optprob2 = OptimizationProblem(
123-
optfn2, rand(4), (odeprob, timesteps, data), lb = 0.1zeros(4), ub = 3ones(4))
124-
sol = solve(optprob2, BFGS())
125-
```
126-
127110
# Re-creating the problem
128111

129112
There are multiple ways to re-create a problem with new state/parameter values. We will go

docs/src/tutorials/initialization.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,73 @@ long enough you will see that `λ = 0` is required for this equation, but since
203203
problem constructor. Additionally, any warning about not being fully determined can
204204
be suppressed via passing `warn_initialize_determined = false`.
205205

206+
## Constant constraints in initialization
207+
208+
Consider the pendulum system again:
209+
210+
```@repl init
211+
equations(pend)
212+
observed(pend)
213+
```
214+
215+
Suppose we want to solve the same system with multiple different initial
216+
y-velocities from a given position.
217+
218+
```@example init
219+
prob = ODEProblem(
220+
pend, [x => 1, D(y) => 0], (0.0, 1.5), [g => 1], guesses = [λ => 0, y => 1, x => 1])
221+
sol1 = solve(prob, Rodas5P())
222+
```
223+
224+
```@example init
225+
sol1[D(y), 1]
226+
```
227+
228+
Repeatedly re-creating the `ODEProblem` with different values of `D(y)` and `x` or
229+
repeatedly calling `remake` is slow. Instead, for any `variable => constant` constraint
230+
in the `ODEProblem` initialization (whether provided to the `ODEProblem` constructor or
231+
a default value) we can update the `constant` value. ModelingToolkit refers to these
232+
values using the `Initial` operator. For example:
233+
234+
```@example init
235+
prob.ps[[Initial(x), Initial(D(y))]]
236+
```
237+
238+
To solve with a different starting y-velocity, we can simply do
239+
240+
```@example init
241+
prob.ps[Initial(D(y))] = -0.1
242+
sol2 = solve(prob, Rodas5P())
243+
```
244+
245+
```@example init
246+
sol2[D(y), 1]
247+
```
248+
249+
Note that this _only_ applies for constant constraints for the current ODEProblem.
250+
For example, `D(x)` does not have a constant constraint - it is solved for by
251+
initialization. Thus, mutating `Initial(D(x))` does not have any effect:
252+
253+
```@repl init
254+
sol2[D(x), 1]
255+
prob.ps[Initial(D(x))] = 1.0
256+
sol3 = solve(prob, Rodas5P())
257+
sol3[D(x), 1]
258+
```
259+
260+
To enforce this constraint, we would have to `remake` the problem (or construct a new one).
261+
262+
```@repl init
263+
prob2 = remake(prob; u0 = [y => 0.0, D(x) => 0.0, x => nothing, D(y) => nothing]);
264+
sol4 = solve(prob2, Rodas5P())
265+
sol4[D(x), 1]
266+
```
267+
268+
Note the need to provide `x => nothing, D(y) => nothing` to override the previously
269+
provided initial conditions. Since `remake` is a partial update, the constraints provided
270+
to it are merged with the ones already present in the problem. Existing constraints can be
271+
removed by providing a value of `nothing`.
272+
206273
## Initialization of parameters
207274

208275
Parameters may also be treated as unknowns in the initialization system. Doing so works
@@ -231,6 +298,9 @@ constraints provided to it. The new values will be combined with the original
231298
variable-value mapping provided to `ODEProblem` and used to construct the initialization
232299
problem.
233300

301+
The variable on the left hand side of all parameter dependencies also has an `Initial`
302+
variant, which is used if a constant constraint is provided for the variable.
303+
234304
### Parameter initialization by example
235305

236306
Consider the following system, where the sum of two unknowns is a constant parameter

ext/MTKBifurcationKitExt.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,10 @@ function BifurcationKit.BifurcationProblem(nsys::NonlinearSystem,
9797
@set! nsys.index_cache = nothing # force usage of a parameter vector instead of `MTKParameters`
9898
# Creates F and J functions.
9999
ofun = NonlinearFunction(nsys; jac = jac)
100-
F = ofun.f
100+
F = let f = ofun.f
101+
_f(resid, u, p) = (f(resid, u, p); resid)
102+
_f(u, p) = f(u, p)
103+
end
101104
J = jac ? ofun.jac : nothing
102105

103106
# Converts the input state guess.
@@ -136,6 +139,7 @@ function BifurcationKit.BifurcationProblem(nsys::NonlinearSystem,
136139
args...;
137140
record_from_solution = record_from_solution,
138141
J = J,
142+
inplace = true,
139143
kwargs...)
140144
end
141145

ext/MTKChainRulesCoreExt.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ end
1616

1717
function subset_idxs(idxs, portion, template)
1818
ntuple(Val(length(template))) do subi
19-
[Base.tail(idx.idx) for idx in idxs if idx.portion == portion && idx.idx[1] == subi]
19+
result = [Base.tail(idx.idx)
20+
for idx in idxs if idx.portion == portion && idx.idx[1] == subi]
21+
if isempty(result)
22+
result = []
23+
end
24+
result
2025
end
2126
end
2227

src/ModelingToolkit.jl

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ using Compat
4545
using AbstractTrees
4646
using DiffEqBase, SciMLBase, ForwardDiff
4747
using SciMLBase: StandardODEProblem, StandardNonlinearProblem, handle_varmap, TimeDomain,
48-
PeriodicClock, Clock, SolverStepClock, Continuous
48+
PeriodicClock, Clock, SolverStepClock, Continuous, OverrideInit, NoInit
4949
using Distributed
5050
import JuliaFormatter
5151
using MLStyle
@@ -56,6 +56,7 @@ using RecursiveArrayTools
5656
import Graphs: SimpleDiGraph, add_edge!, incidence_matrix
5757
import BlockArrays: BlockArray, BlockedArray, Block, blocksize, blocksizes, blockpush!,
5858
undef_blocks, blocks
59+
using OffsetArrays: Origin
5960
import CommonSolve
6061
import EnumX
6162

@@ -153,6 +154,7 @@ include("systems/imperative_affect.jl")
153154
include("systems/callbacks.jl")
154155
include("systems/codegen_utils.jl")
155156
include("systems/problem_utils.jl")
157+
include("linearization.jl")
156158

157159
include("systems/nonlinear/nonlinearsystem.jl")
158160
include("systems/nonlinear/homotopy_continuation.jl")
@@ -261,7 +263,8 @@ export Term, Sym
261263
export SymScope, LocalScope, ParentScope, DelayParentScope, GlobalScope
262264
export independent_variable, equations, controls, observed, full_equations
263265
export initialization_equations, guesses, defaults, parameter_dependencies, hierarchy
264-
export structural_simplify, expand_connections, linearize, linearization_function
266+
export structural_simplify, expand_connections, linearize, linearization_function,
267+
LinearizationProblem
265268

266269
export calculate_jacobian, generate_jacobian, generate_function, generate_custom_function
267270
export calculate_control_jacobian, generate_control_jacobian
@@ -281,7 +284,7 @@ export toexpr, get_variables
281284
export simplify, substitute
282285
export build_function
283286
export modelingtoolkitize
284-
export generate_initializesystem
287+
export generate_initializesystem, Initial
285288

286289
export alg_equations, diff_equations, has_alg_equations, has_diff_equations
287290
export get_alg_eqs, get_diff_eqs, has_alg_eqs, has_diff_eqs

src/inputoutput.jl

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ function generate_control_function(sys::AbstractODESystem, inputs = unbound_inpu
211211
sys, _ = io_preprocessing(sys, inputs, []; simplify, kwargs...)
212212

213213
dvs = unknowns(sys)
214-
ps = parameters(sys)
214+
ps = parameters(sys; initial_parameters = true)
215215
ps = setdiff(ps, inputs)
216216
if disturbance_inputs !== nothing
217217
# remove from inputs since we do not want them as actual inputs to the dynamics
@@ -234,16 +234,14 @@ function generate_control_function(sys::AbstractODESystem, inputs = unbound_inpu
234234
[eq.rhs for eq in eqs]
235235

236236
# TODO: add an optional check on the ordering of observed equations
237-
u = map(x -> time_varying_as_func(value(x), sys), dvs)
238-
p = map(x -> time_varying_as_func(value(x), sys), ps)
239-
p = reorder_parameters(sys, p)
237+
p = reorder_parameters(sys, ps)
240238
t = get_iv(sys)
241239

242240
# pre = has_difference ? (ex -> ex) : get_postprocess_fbody(sys)
243241
if disturbance_argument
244-
args = (u, inputs, p..., t, disturbance_inputs)
242+
args = (dvs, inputs, p..., t, disturbance_inputs)
245243
else
246-
args = (u, inputs, p..., t)
244+
args = (dvs, inputs, p..., t)
247245
end
248246
if implicit_dae
249247
ddvs = map(Differential(get_iv(sys)), dvs)
@@ -252,6 +250,7 @@ function generate_control_function(sys::AbstractODESystem, inputs = unbound_inpu
252250
f = build_function_wrapper(sys, rhss, args...; p_start = 3 + implicit_dae,
253251
p_end = length(p) + 2 + implicit_dae)
254252
f = eval_or_rgf.(f; eval_expression, eval_module)
253+
ps = setdiff(parameters(sys), inputs, disturbance_inputs)
255254
(; f, dvs, ps, io_sys = sys)
256255
end
257256

0 commit comments

Comments
 (0)