1
+ # ! format: off
2
+
3
+ # ## Prepares Tests ###
4
+
5
+ # Fetch packages
6
+ using ModelingToolkit, JumpProcesses, NonlinearSolve, OrdinaryDiffEq, SteadyStateDiffEq, StochasticDiffEq, Test
7
+ using ModelingToolkit: t_nounits as t, D_nounits as D
8
+
9
+ # Sets rnd number.
10
+ using StableRNGs
11
+ rng = StableRNG (12345 )
12
+ seed = rand (rng, 1 : 100 )
13
+
14
+ # ## Basic Tests ###
15
+
16
+ # Prepares a models and initial conditions/parameters (of different forms) to be used as problem inputs.
17
+ begin
18
+ # Prepare system components.
19
+ @parameters kp kd k1 k2= 0.5 Z0
20
+ @variables X (t) Y (t) Z (t) = Z0
21
+ alg_eqs = [
22
+ 0 ~ kp - k1* X + k2* Y - kd* X,
23
+ 0 ~ - k1* Y + k1* X - k2* Y + k2* Z,
24
+ 0 ~ k1* Y - k2* Z
25
+ ]
26
+ diff_eqs = [
27
+ D (X) ~ kp - k1* X + k2* Y - kd* X,
28
+ D (Y) ~ - k1* Y + k1* X - k2* Y + k2* Z,
29
+ D (Z) ~ k1* Y - k2* Z
30
+ ]
31
+ noise_eqs = fill (0.01 , 3 , 6 )
32
+ jumps = [
33
+ MassActionJump (kp, Pair{Symbolics. BasicSymbolic{Real}, Int64}[], [X => 1 ]),
34
+ MassActionJump (kd, [X => 1 ], [X => - 1 ]),
35
+ MassActionJump (k1, [X => 1 ], [X => - 1 , Y => 1 ]),
36
+ MassActionJump (k2, [Y => 1 ], [X => 1 , Y => - 1 ]),
37
+ MassActionJump (k1, [Y => 1 ], [Y => - 1 , Z => 1 ]),
38
+ MassActionJump (k2, [Z => 1 ], [Y => 1 , Z => - 1 ])
39
+ ]
40
+
41
+ # Create systems (without structural_simplify, since that might modify systems to affect intended tests).
42
+ osys = complete (ODESystem (diff_eqs, t; name = :osys ))
43
+ ssys = complete (SDESystem (diff_eqs, noise_eqs, t, [X, Y, Z], [kp, kd, k1, k2]; name = :ssys ))
44
+ jsys = complete (JumpSystem (jumps, t, [X, Y, Z], [kp, kd, k1, k2]; name = :jsys ))
45
+ nsys = complete (NonlinearSystem (alg_eqs; name = :nsys ))
46
+
47
+ u0_alts = [
48
+ # Vectors not providing default values.
49
+ [X => 4 , Y => 5 ],
50
+ [osys. X => 4 , osys. Y => 5 ],
51
+ # Vectors providing default values.
52
+ [X => 4 , Y => 5 , Z => 10 ],
53
+ [osys. X => 4 , osys. Y => 5 , osys. Z => 10 ],
54
+ # Dicts not providing default values.
55
+ Dict ([X => 4 , Y => 5 ]),
56
+ Dict ([osys. X => 4 , osys. Y => 5 ]),
57
+ # Dicts providing default values.
58
+ Dict ([X => 4 , Y => 5 , Z => 10 ]),
59
+ Dict ([osys. X => 4 , osys. Y => 5 , osys. Z => 10 ]),
60
+ # Tuples not providing default values.
61
+ (X => 4 , Y => 5 ),
62
+ (osys. X => 4 , osys. Y => 5 ),
63
+ # Tuples providing default values.
64
+ (X => 4 , Y => 5 , Z => 10 ),
65
+ (osys. X => 4 , osys. Y => 5 , osys. Z => 10 ),
66
+ ]
67
+ tspan = (0.0 , 10.0 )
68
+ p_alts = [
69
+ # Vectors not providing default values.
70
+ [kp => 1.0 , kd => 0.1 , k1 => 0.25 , Z0 => 10 ],
71
+ [osys. kp => 1.0 , osys. kd => 0.1 , osys. k1 => 0.25 , osys. Z0 => 10 ],
72
+ # Vectors providing default values.
73
+ [kp => 1.0 , kd => 0.1 , k1 => 0.25 , k2 => 0.5 , Z0 => 10 ],
74
+ [osys. kp => 1.0 , osys. kd => 0.1 , osys. k1 => 0.25 , osys. k2 => 0.5 , osys. Z0 => 10 ],
75
+ # Dicts not providing default values.
76
+ Dict ([kp => 1.0 , kd => 0.1 , k1 => 0.25 , Z0 => 10 ]),
77
+ Dict ([osys. kp => 1.0 , osys. kd => 0.1 , osys. k1 => 0.25 , osys. Z0 => 10 ]),
78
+ # Dicts providing default values.
79
+ Dict ([kp => 1.0 , kd => 0.1 , k1 => 0.25 , k2 => 0.5 , Z0 => 10 ]),
80
+ Dict ([osys. kp => 1.0 , osys. kd => 0.1 , osys. k1 => 0.25 , osys. k2 => 0.5 , osys. Z0 => 10 ]),
81
+ # Tuples not providing default values.
82
+ (kp => 1.0 , kd => 0.1 , k1 => 0.25 , Z0 => 10 ),
83
+ (osys. kp => 1.0 , osys. kd => 0.1 , osys. k1 => 0.25 , osys. Z0 => 10 ),
84
+ # Tuples providing default values.
85
+ (kp => 1.0 , kd => 0.1 , k1 => 0.25 , k2 => 0.5 , Z0 => 10 ),
86
+ (osys. kp => 1.0 , osys. kd => 0.1 , osys. k1 => 0.25 , osys. k2 => 0.5 , osys. Z0 => 10 ),
87
+ ]
88
+ end
89
+
90
+ # Perform ODE simulations (singular and ensemble).
91
+ let
92
+ # Creates normal and ensemble problems.
93
+ base_oprob = ODEProblem (osys, u0_alts[1 ], tspan, p_alts[1 ])
94
+ base_sol = solve (base_oprob, Tsit5 (); saveat = 1.0 )
95
+ base_eprob = EnsembleProblem (base_oprob)
96
+ base_esol = solve (base_eprob, Tsit5 (); trajectories = 2 , saveat = 1.0 )
97
+
98
+ # Simulates problems for all input types, checking that identical solutions are found.
99
+ for u0 in u0_alts, p in p_alts
100
+ oprob = remake (base_oprob; u0, p)
101
+ @test base_sol == solve (oprob, Tsit5 (); saveat = 1.0 )
102
+ eprob = remake (base_eprob; u0, p)
103
+ @test base_esol == solve (eprob, Tsit5 (); trajectories = 2 , saveat = 1.0 )
104
+ end
105
+ end
106
+
107
+ # Perform SDE simulations (singular and ensemble).
108
+ let
109
+ # Creates normal and ensemble problems.
110
+ base_sprob = SDEProblem (ssys, u0_alts[1 ], tspan, p_alts[1 ])
111
+ base_sol = solve (base_sprob, ImplicitEM (); seed, saveat = 1.0 )
112
+ base_eprob = EnsembleProblem (base_sprob)
113
+ base_esol = solve (base_eprob, ImplicitEM (); seed, trajectories = 2 , saveat = 1.0 )
114
+
115
+ # Simulates problems for all input types, checking that identical solutions are found.
116
+ @test_broken false # first remake in subsequent test yields a `ERROR: type Nothing has no field portion`.
117
+ # for u0 in u0_alts, p in p_alts
118
+ # sprob = remake(base_sprob; u0, p)
119
+ # @test base_sol == solve(sprob, ImplicitEM(); seed, saveat = 1.0)
120
+ # eprob = remake(base_eprob; u0, p)
121
+ # @test base_esol == solve(eprob, ImplicitEM(); seed, trajectories = 2, saveat = 1.0)
122
+ # end
123
+ end
124
+
125
+ # Perform jump simulations (singular and ensemble).
126
+ let
127
+ # Creates normal and ensemble problems.
128
+ base_dprob = DiscreteProblem (jsys, u0_alts[1 ], tspan, p_alts[1 ])
129
+ base_jprob = JumpProblem (jsys, base_dprob, Direct (); rng)
130
+ base_sol = solve (base_jprob, SSAStepper (); seed, saveat = 1.0 )
131
+ base_eprob = EnsembleProblem (base_jprob)
132
+ base_esol = solve (base_eprob, SSAStepper (); seed, trajectories = 2 , saveat = 1.0 )
133
+
134
+ # Simulates problems for all input types, checking that identical solutions are found.
135
+ @test_broken false # first remake in subsequent test yields a `ERROR: type Nothing has no field portion`.
136
+ # for u0 in u0_alts, p in p_alts
137
+ # jprob = remake(base_jprob; u0, p)
138
+ # @test base_sol == solve(base_jprob, SSAStepper(); seed, saveat = 1.0)
139
+ # eprob = remake(base_eprob; u0, p)
140
+ # @test base_esol == solve(eprob, SSAStepper(); seed, trajectories = 2, saveat = 1.0)
141
+ # end
142
+ end
143
+
144
+ # Solves a nonlinear problem (EnsembleProblems are not possible for these).
145
+ let
146
+ base_nlprob = NonlinearProblem (nsys, u0_alts[1 ], p_alts[1 ])
147
+ base_sol = solve (base_nlprob, NewtonRaphson ())
148
+ for u0 in u0_alts, p in p_alts
149
+ nlprob = remake (base_nlprob; u0, p)
150
+ @test base_sol == solve (nlprob, NewtonRaphson ())
151
+ end
152
+ end
153
+
154
+ # Perform steady state simulations (singular and ensemble).
155
+ let
156
+ # Creates normal and ensemble problems.
157
+ base_ssprob = SteadyStateProblem (osys, u0_alts[1 ], p_alts[1 ])
158
+ base_sol = solve (base_ssprob, DynamicSS (Tsit5 ()))
159
+ base_eprob = EnsembleProblem (base_ssprob)
160
+ base_esol = solve (base_eprob, DynamicSS (Tsit5 ()); trajectories = 2 )
161
+
162
+ # Simulates problems for all input types, checking that identical solutions are found.
163
+ for u0 in u0_alts, p in p_alts
164
+ ssprob = remake (base_ssprob; u0, p)
165
+ @test base_sol == solve (ssprob, DynamicSS (Tsit5 ()))
166
+ eprob = remake (base_eprob; u0, p)
167
+ @test base_esol == solve (eprob, DynamicSS (Tsit5 ()); trajectories = 2 )
168
+ end
169
+ end
170
+
171
+
172
+ # ## Checks Errors On Faulty Inputs ###
173
+
174
+ # Checks various erroneous problem inputs, ensuring that these throw errors.
175
+ let
176
+ # Prepare system components.
177
+ @parameters k1 k2 k3
178
+ @variables X1 (t) X2 (t) X3 (t)
179
+ alg_eqs = [
180
+ 0 ~ - k1* X1 + k2* X2,
181
+ 0 ~ k1* X1 - k2* X2
182
+ ]
183
+ diff_eqs = [
184
+ D (X1) ~ - k1* X1 + k2* X2,
185
+ D (X2) ~ k1* X1 - k2* X2
186
+ ]
187
+ noise_eqs = fill (0.01 , 2 , 2 )
188
+ jumps = [
189
+ MassActionJump (k1, [X1 => 1 ], [X1 => - 1 , X2 => 1 ]),
190
+ MassActionJump (k2, [X2 => 1 ], [X1 => 1 , X2 => - 1 ])
191
+ ]
192
+
193
+ # Create systems (without structural_simplify, since that might modify systems to affect intended tests).
194
+ osys = complete (ODESystem (diff_eqs, t; name = :osys ))
195
+ ssys = complete (SDESystem (diff_eqs, noise_eqs, t, [X1, X2], [k1, k2]; name = :ssys ))
196
+ jsys = complete (JumpSystem (jumps, t, [X1, X2], [k1, k2]; name = :jsys ))
197
+ nsys = complete (NonlinearSystem (alg_eqs; name = :nsys ))
198
+
199
+ # Declares valid initial conditions and parameter values
200
+ u0_valid = [X1 => 1 , X2 => 2 ]
201
+ ps_valid = [k1 => 0.5 , k2 => 0.1 ]
202
+
203
+ # Declares invalid initial conditions and parameters. This includes both cases where values are
204
+ # missing, or additional ones are given. Includes vector/Tuple/Dict forms.
205
+ u0s_invalid = [
206
+ # Missing a value.
207
+ [X1 => 1 ],
208
+ [osys. X1 => 1 ],
209
+ Dict ([X1 => 1 ]),
210
+ Dict ([osys. X1 => 1 ]),
211
+ (X1 => 1 ),
212
+ (osys. X1 => 1 ),
213
+ # Contain an additional value.
214
+ [X1 => 1 , X2 => 2 , X3 => 3 ],
215
+ Dict ([X1 => 1 , X2 => 2 , X3 => 3 ]),
216
+ (X1 => 1 , X2 => 2 , X3 => 3 ),
217
+ ]
218
+ ps_invalid = [
219
+ # Missing a value.
220
+ [k1 => 1.0 ],
221
+ [osys. k1 => 1.0 ],
222
+ Dict ([k1 => 1.0 ]),
223
+ Dict ([osys. k1 => 1.0 ]),
224
+ (k1 => 1.0 ),
225
+ (osys. k1 => 1.0 ),
226
+ # Contain an additional value.
227
+ [k1 => 1.0 , k2 => 2.0 , k3 => 3.0 ],
228
+ Dict ([k1 => 1.0 , k2 => 2.0 , k3 => 3.0 ]),
229
+ (k1 => 1.0 , k2 => 2.0 , k3 => 3.0 ),
230
+ ]
231
+
232
+ # Loops through all potential parameter sets, checking their inputs yield errors.
233
+ # Broken tests are due to this issue: https://github.com/SciML/ModelingToolkit.jl/issues/2779
234
+ for ps in [ps_valid; ps_invalid], u0 in [u0_valid; u0s_invalid]
235
+ # Handles problems with/without tspan separately. Special check ensuring that valid inputs passes.
236
+ for (xsys, XProblem) in zip ([osys, ssys, jsys], [ODEProblem, SDEProblem, DiscreteProblem])
237
+ if (ps == ps_valid) && (u0 == u0_valid)
238
+ XProblem (xsys, u0, (0.0 , 1.0 ), ps); @test true ;
239
+ else
240
+ @test_broken false
241
+ continue
242
+ @test_throws Exception XProblem (xsys, u0, (0.0 , 1.0 ), ps)
243
+ end
244
+ end
245
+ for (xsys, XProblem) in zip ([nsys, osys], [NonlinearProblem, SteadyStateProblem])
246
+ if (ps == ps_valid) && (u0 == u0_valid)
247
+ XProblem (xsys, u0, ps); @test true ;
248
+ else
249
+ @test_broken false
250
+ continue
251
+ @test_throws Exception XProblem (xsys, u0, ps)
252
+ end
253
+ end
254
+ end
255
+ end
0 commit comments