Skip to content

Commit 91cf6cc

Browse files
authored
Informative error on nonexisting symbolic parameter (#232)
* Informative error on nonexisting symbolic parameter * mention init cond as parameters * correct typo * add a new function for named current parameters * organize MTK tests better * add reinit_dae = false See SciML/ModelingToolkit.jl#3451 * remove `named_parametersd` Needs MTK to be defined * allow kw propagation in reinit * fix reinit for SDE * simplify ODE test dependency * continuous tests pass * all tests pass? * don't forget array for SDEs * abvoid method overwrites in tests * remove RODAS usage * fix jacobian (?) * fix jacian? * add back the type check only way to ensure the symbolic jacobian was extracted. * correct version
1 parent 65443a8 commit 91cf6cc

13 files changed

+166
-136
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# v3.14.0
2+
3+
- `set_parameter!` and `current_parameter` will now throw an informative error message explicitly naming the parameter if the user tries to get/set a symbolic parameter that does not exist in the MTK-generated dynamical system.
4+
- Fixed a plethora of bugs and test failures associated with updates in the SciML ecosystem.
5+
16
# v3.13.0
27

38
- `jacobian` will now re-use symbolically generated Jacobian functions if the dynamical system is made through a ModelingToolkit.jl-generated `DEProblem`.

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "DynamicalSystemsBase"
22
uuid = "6e36e845-645a-534a-86f2-f5d4aa5a06b4"
33
repo = "https://github.com/JuliaDynamics/DynamicalSystemsBase.jl.git"
4-
version = "3.13.3"
4+
version = "3.14.0"
55

66
[deps]
77
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"

src/core/dynamicalsystem_interface.jl

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,12 @@ The referenced MTK model corresponding to the dynamical system can be obtained w
5656
5757
See also the DynamicalSystems.jl tutorial online for an example.
5858
59-
!!! warn "ModelingToolkit.jl v9"
60-
In ModelingToolkit.jl v9 the default `split` behavior of the parameter container
61-
is `true`. This means that the parameter container is no longer a `Vector{Float64}`
62-
by default, which means that you cannot use integers to access parameters.
63-
It is recommended to keep `split = true` (default) and only access
64-
parameters via their symbolic parameter binding.
65-
Use `structural_simplify(sys; split = false)` to allow accessing parameters
66-
with integers again.
59+
!!! warn "Initial conditions and parameters"
60+
ModelingToolkit.jl treats initial conditions of all variables as additional parameters.
61+
This is because its terminology is aimed primarily at initial value problems rather
62+
than dynamical systems. It is strongly recommended when making a dynamical system from
63+
an MTK model to only access parameters via symbols, and to not use the `split = false`
64+
keyword when creating the problem type from the MTK model.
6765
6866
## API
6967
@@ -79,8 +77,8 @@ unless when developing new algorithm implementations that use dynamical systems.
7977
### API - obtain information
8078
8179
- `ds(t)` with `ds` an instance of `DynamicalSystem`: return the state of `ds` at time `t`.
82-
For continuous time systems this interpolates and extrapolates,
83-
while for discrete time systems it only works if `t` is the current time.
80+
For continuous time systems this interpolates current and previous time step and is very accurate;
81+
for discrete time systems it only works if `t` is the current time.
8482
- [`current_state`](@ref)
8583
- [`initial_state`](@ref)
8684
- [`observe_state`](@ref)
@@ -194,7 +192,7 @@ Return the state `u` of `ds` _observed_ at "index" `i`. Possibilities are:
194192
symbolic variables.
195193
196194
For [`ProjectedDynamicalSystem`](@ref), this function assumes that the
197-
state of the system is the full state space state, not the projected one
195+
state of the system is the original state space state, not the projected one
198196
(this makes the most sense for allowing MTK-based indexing).
199197
200198
Use [`state_name`](@ref) for an accompanying name.
@@ -247,7 +245,10 @@ function current_parameter(ds::DynamicalSystem, index, p = current_parameters(ds
247245
prob = referrenced_sciml_prob(ds)
248246
if !has_referrenced_model(prob)
249247
return _get_parameter(p, index)
250-
else # symbolic dispatch
248+
else # symbolic parameter
249+
if !SymbolicIndexingInterface.is_parameter(prob, index)
250+
error("Symbolic parameter with name $(index) does not exist in the system.")
251+
end
251252
i = SymbolicIndexingInterface.getp(prob, index)
252253
return i(p)
253254
end
@@ -431,7 +432,10 @@ function _set_parameter!(ds::DynamicalSystem, index, value, p = current_paramete
431432
else
432433
setproperty!(p, index, value)
433434
end
434-
else
435+
else # symbolic parameter
436+
if !SymbolicIndexingInterface.is_parameter(prob, index)
437+
error("Symbolic parameter with name $(index) does not exist in the system.")
438+
end
435439
set! = SymbolicIndexingInterface.setp(prob, index)
436440
set!(p, value)
437441
end

src/core_systems/continuous_time_ode.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,10 @@ set_state!(ds::CoupledODEs, u::AbstractArray) = (set_state!(ds.integ, u); ds)
142142
SciMLBase.step!(ds::CoupledODEs, args...) = (step!(ds.integ, args...); ds)
143143

144144
function SciMLBase.reinit!(ds::ContinuousTimeDynamicalSystem, u::AbstractArray = initial_state(ds);
145-
p = current_parameters(ds), t0 = initial_time(ds)
145+
p = current_parameters(ds), t0 = initial_time(ds), kw...
146146
)
147147
set_parameters!(ds, p)
148-
reinit!(ds.integ, u; reset_dt = true, t0)
148+
reinit!(ds.integ, u; reinit_dae = false, reset_dt = true, t0, kw...)
149149
return ds
150150
end
151151

src/core_systems/continuous_time_sde.jl

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ where
1313
``\\mathbf{u}(t)`` is the state vector at time ``t``, ``\\mathbf{f}`` describes the
1414
deterministic dynamics, and the noise term
1515
``\\mathbf{g}(\\mathbf{u}, p, t) \\text{d}\\mathcal{N}_t`` describes
16-
the stochastic forcing in terms of a noise function (or *diffusion function*)
16+
the stochastic forcing in terms of a noise function (or *diffusion function*)
1717
``\\mathbf{g}`` and a noise process ``\\mathcal{N}_t``. The parameters of the functions
1818
``\\mathcal{f}`` and ``\\mathcal{g}`` are contained in the vector ``p``.
1919
@@ -55,7 +55,7 @@ Commonly, this is a matrix or sparse matrix. If this is not given, it
5555
defaults to `nothing`, which means the `g` should be interpreted as being diagonal.
5656
5757
The noise process can be specified via `noise_process`. It defaults to a standard Wiener
58-
process (Gaussian white noise).
58+
process (Gaussian white noise).
5959
For details on defining noise processes, see the docs of [DiffEqNoiseProcess.jl
6060
](https://docs.sciml.ai/DiffEqNoiseProcess/stable/). A complete list of the pre-defined
6161
processes can be found [here](https://docs.sciml.ai/DiffEqNoiseProcess/stable/noise_processes/).
@@ -111,4 +111,12 @@ struct CoupledSDEs{IIP,D,I,P} <: ContinuousTimeDynamicalSystem
111111
p0::P
112112
diffeq # isn't parameterized because it is only used for display
113113
noise_type::NamedTuple
114+
end
115+
116+
function SciMLBase.reinit!(ds::CoupledSDEs, u::AbstractArray = initial_state(ds);
117+
p = current_parameters(ds), t0 = initial_time(ds), kw...
118+
)
119+
set_parameters!(ds, p)
120+
reinit!(ds.integ, u; t0, kw...)
121+
return ds
114122
end

src/core_systems/jacobian.jl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import ForwardDiff
66
jacobian(ds::CoreDynamicalSystem)
77
88
Construct the Jacobian rule for the dynamical system `ds`.
9-
If the system already has a Jacobian rule constructed via ModelingToolkit it returns this,
9+
If the system already has a Jacobian rule constructed via ModelingToolkit.jl it returns this,
1010
otherwise it constructs the Jacobian rule with automatic differentiation using module
1111
[`ForwardDiff`](https://github.com/JuliaDiff/ForwardDiff.jl).
1212
@@ -21,6 +21,17 @@ at the state `u`, parameters `p` and time `t` and save the result in `J0`.
2121
"""
2222
function jacobian(ds::CoreDynamicalSystem{IIP}) where {IIP}
2323
if ds isa ContinuousTimeDynamicalSystem
24+
# TODO: This is the correct API way to obtain the Jacobian,
25+
# however it relies on MTK dependency, so we can't do it.
26+
# if has_referrenced_model(ds)
27+
# model = referrenced_sciml_model(ds)
28+
# Joop, Jiip = generate_jacobian(model; expression = Val{false})
29+
# if IIP
30+
# jac = Jiip
31+
# else
32+
# jac = Joop
33+
# end
34+
# end
2435
prob = referrenced_sciml_prob(ds)
2536
if prob.f isa SciMLBase.AbstractDiffEqFunction && !isnothing(prob.f.jac)
2637
jac = prob.f.jac

test/Project.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ DiffEqNoiseProcess = "77a26b50-5914-5dd7-bc55-306e6241c503"
33
DynamicalSystemsBase = "6e36e845-645a-534a-86f2-f5d4aa5a06b4"
44
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
55
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
6-
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
6+
OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a"
7+
OrdinaryDiffEqVerner = "79d7bb75-1356-48c1-b8c0-6832512096c2"
78
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
89
StochasticDiffEq = "789caeaf-c7a9-5a7d-9973-96adeb23e2a0"
910
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

test/continuous.jl

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using DynamicalSystemsBase, Test
22

3-
using OrdinaryDiffEq: Vern9, ODEProblem, Rodas5, Tsit5
3+
using OrdinaryDiffEqTsit5: ODEProblem, Tsit5
4+
using OrdinaryDiffEqVerner: Vern9
45

56
include("test_system_function.jl")
67

@@ -25,14 +26,9 @@ p0 = [10, 28, 8/3]
2526

2627
lorenz_oop = CoupledODEs(lorenz_rule, u0, p0)
2728
lorenz_iip = CoupledODEs(ODEProblem(lorenz_rule_iip, copy(u0), (0.0, Inf), p0))
28-
lorenz_vern = CoupledODEs(lorenz_rule, u0, p0;
29-
diffeq = (alg = Vern9(), abstol = 1e-9, reltol = 1e-9)
30-
)
3129

32-
for (ds, iip) in zip((lorenz_oop, lorenz_iip, lorenz_vern), (false, true, false))
33-
34-
name = (ds === lorenz_vern) ? "lorvern" : "lorenz"
35-
@testset "$(name) IIP=$(iip)" begin
30+
for (ds, iip) in zip((lorenz_oop, lorenz_iip), (false, true))
31+
@testset "IIP=$(iip)" begin
3632
@test dynamic_rule(ds) == (iip ? lorenz_rule_iip : lorenz_rule)
3733
test_dynamical_system(ds, u0, p0; idt = false, iip=iip)
3834
end
@@ -42,20 +38,9 @@ end
4238
lorenz_oop = CoupledODEs(lorenz_rule, u0, p0)
4339
@test lorenz_oop.integ.alg isa Tsit5
4440

45-
lorenz_vern = CoupledODEs(lorenz_rule, u0, p0;
46-
diffeq = (alg = Vern9(), verbose = false, abstol = 1e-9, reltol = 1e-9)
47-
)
48-
@test lorenz_vern.integ.alg isa Vern9
49-
@test lorenz_vern.integ.opts.verbose == false
50-
51-
# also test ODEproblem creation
52-
prob = lorenz_vern.integ.sol.prob
53-
54-
ds = CoupledODEs(prob, (alg=Rodas5(autodiff=false), abstol=0.0, reltol=1e-6, verbose=false))
55-
56-
@test ds.integ.alg isa Rodas5
41+
prob = lorenz_oop.integ.sol.prob
42+
ds = CoupledODEs(prob, (alg=Vern9(), abstol=0.0, reltol=1e-6, verbose=false))
43+
@test ds.integ.alg isa Vern9
5744
@test ds.integ.opts.verbose == false
5845

59-
@test_throws ArgumentError CoupledODEs(prob; diffeq = (alg=Rodas5(autodiff=false), ))
60-
6146
end

test/jacobian.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ end
3030

3131
@testset "MTK Jacobian" begin
3232
using ModelingToolkit
33-
using ModelingToolkit: Num, RuntimeGeneratedFunctions.RuntimeGeneratedFunction
33+
using ModelingToolkit: Num, GeneratedFunctionWrapper
3434
using DynamicalSystemsBase: SciMLBase
3535
@independent_variables t
3636
@variables u(t)[1:2]
@@ -42,10 +42,10 @@ end
4242
sys = structural_simplify(sys)
4343

4444
prob = ODEProblem(sys, [1.0, 1.0], (0.0, 1.0); jac=true)
45-
ode = CoupledODEs(prob)
45+
ds = CoupledODEs(prob)
4646

47-
jac = jacobian(ode)
48-
@test jac.jac_oop isa RuntimeGeneratedFunction
47+
jac = jacobian(ds)
48+
@test jac isa GeneratedFunctionWrapper
4949
@test jac([1.0, 1.0], [], 0.0) == [3 0;0 -3]
5050

5151
@testset "CoupledSDEs" begin
@@ -60,7 +60,7 @@ end
6060
sde = CoupledSDEs(prob)
6161

6262
jac = jacobian(sde)
63-
@test jac.jac_oop isa RuntimeGeneratedFunction
64-
@test jac([1.0, 1.0], [], 0.0) == [3 0;0 -3]
63+
@test jac isa GeneratedFunctionWrapper
64+
@test jac([1.0, 1.0], [], 0.0) == [3 0; 0 -3]
6565
end
6666
end

0 commit comments

Comments
 (0)