Skip to content

Commit 84bf46e

Browse files
authored
Merge branch 'master' into noise_types_example
2 parents c74f3f6 + 0d6b76a commit 84bf46e

Some content is hidden

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

44 files changed

+1064
-499
lines changed

HISTORY.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
solver that auto-switches between explicit and implicit methods, install `OrdinaryDiffEqDefault`. To
1414
use `Tsit5` install `OrdinaryDiffEqTsit5`, etc. The possible sub-libraries, each containing different solvers,
1515
can be viewed [here](https://github.com/SciML/OrdinaryDiffEq.jl/tree/master/lib).
16+
- It should now be safe to use `remake` on problems which have had conservation laws removed.
17+
The warning regarding this when eliminating conservation laws have been removed (in conjunction
18+
with the `remove_conserved_warn` argument for disabling this warning).
1619
- New formula for inferring variables from equations (declared using the `@equations` options) in the DSL. The order of inference of species/variables/parameters is now:
1720
(1) Every symbol explicitly declared using `@species`, `@variables`, and `@parameters` are assigned to the correct category.
1821
(2) Every symbol used as a reaction reactant is inferred as a species.
@@ -51,8 +54,6 @@
5154
- Functional (e.g. time-dependent) parameters can now be used in Catalyst models. These can e.g. be used to incorporate arbitrary time-dependent functions (as a parameter) in a model. For more details on how to use these, please read: https://docs.sciml.ai/Catalyst/stable/model_creation/functional_parameters/.
5255
- Scoped species/variables/parameters are now treated similar to the latest MTK
5356
releases (≥ 9.49).
54-
- The structural identifiability extension is currently disabled due to issues
55-
StructuralIdentifiability has with Julia 1.10.5 and 1.11.
5657
- A tutorial on making interactive plot displays using Makie has been added.
5758
- The BifurcationKit extension has been updated to v.4.
5859
- There is a new DSL option `@require_declaration` that will turn off automatic inferring for species, parameters, and variables in the DSL. For example, the following will now error:

Project.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ BifurcationKit = "0.4.4"
4949
CairoMakie = "0.12, 0.13"
5050
Combinatorics = "1.0.2"
5151
DataStructures = "0.18"
52-
DiffEqBase = "6.159.0"
52+
DiffEqBase = "6.165.0"
5353
DocStringExtensions = "0.8, 0.9"
5454
DynamicPolynomials = "0.5, 0.6"
5555
DynamicQuantities = "0.13.2, 1"
@@ -61,17 +61,17 @@ LaTeXStrings = "1.3.0"
6161
Latexify = "0.16.6"
6262
MacroTools = "0.5.5"
6363
Makie = "0.22.1"
64-
ModelingToolkit = "< 9.60"
64+
ModelingToolkit = "9.69"
6565
NetworkLayout = "0.4.7"
6666
Parameters = "0.12"
6767
Reexport = "0.2, 1.0"
6868
Requires = "1.0"
6969
RuntimeGeneratedFunctions = "0.5.12"
70-
SciMLBase = "2.57.2"
70+
SciMLBase = "2.77"
7171
Setfield = "1"
7272
StructuralIdentifiability = "0.5.11"
73-
SymbolicUtils = "3.20.0"
74-
Symbolics = "6.22"
73+
SymbolicUtils = "3.20"
74+
Symbolics = "6.31.1"
7575
Unitful = "1.12.4"
7676
julia = "1.10"
7777

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,21 @@ Generated models can be used with solvers throughout the broader Julia and
3030
for sensitivity analysis, parameter estimation, machine learning applications,
3131
etc).
3232

33+
## Installation
34+
Catalyst can be installed as follows. Please note, we suggest only installing ModelingToolkit versions 9.59 and earlier for use with Catalyst at this time as changes in ModelingToolkit as of version 9.60 can break various Catalyst functionality.
35+
```julia
36+
using Pkg
37+
38+
# (optional but recommended) create new environment in which to install Catalyst
39+
Pkg.activate("catalyst_environment")
40+
41+
# install ModelingToolkit 9.59
42+
Pkg.add(; name = "ModelingToolkit", version ="9.59")
43+
44+
# install latest Catalyst release
45+
Pkg.add("Catalyst")
46+
```
47+
3348
## Breaking changes and new features
3449

3550
**NOTE:** Version 14 is a breaking release, prompted by the release of ModelingToolkit.jl version 9. This caused several breaking changes in how Catalyst models are represented and interfaced with.

docs/Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ IncompleteLU = "0.2"
6565
JumpProcesses = "9.13.2"
6666
Latexify = "0.16.5"
6767
LinearSolve = "2.30, 3"
68-
ModelingToolkit = "< 9.60"
68+
ModelingToolkit = "9.69"
6969
NetworkLayout = "0.4"
7070
NonlinearSolve = "3.12, 4"
7171
Optim = "1.9"

docs/src/api.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ direct access to the corresponding internal fields of the `ReactionSystem`)
124124
entries in `get_species(rn)` correspond to the first `length(get_species(rn))`
125125
components in `get_unknowns(rn)`.
126126
* `ModelingToolkit.get_ps(rn)` is a vector that collects all the parameters
127-
defined *within* reactions in `rn`.
127+
defined *within* reactions in `rn`. This includes initialisation parameters (which
128+
are added to the system by ModelingToolkit, and not the user).
128129
* `ModelingToolkit.get_eqs(rn)` is a vector that collects all the
129130
[`Reaction`](@ref)s and `Symbolics.Equation` defined within `rn`, ordering all
130131
`Reaction`s before `Equation`s.
Lines changed: 34 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# [Fitting Parameters for an Oscillatory System](@id parameter_estimation)
2-
In this example we will use [Optimization.jl](https://github.com/SciML/Optimization.jl) to fit the parameters of an oscillatory system (the Brusselator) to data. Here, special consideration is taken to avoid reaching a local minimum. Instead of fitting the entire time series directly, we will start with fitting parameter values for the first period, and then use those as an initial guess for fitting the next (and then these to find the next one, and so on). Using this procedure is advantageous for oscillatory systems, and enables us to reach the global optimum.
2+
In this example we will use [Optimization.jl](https://github.com/SciML/Optimization.jl) to fit the parameters of an oscillatory system (the [Brusselator](@ref basic_CRN_library_brusselator)) to data. Here, special consideration is taken to avoid reaching a local minimum. Instead of fitting the entire time series directly, we will start with fitting parameter values for the first period, and then use those as an initial guess for fitting the next (and then these to find the next one, and so on). Using this procedure is advantageous for oscillatory systems, and enables us to reach the global optimum. For more information on fitting ODE parameters to data, please see [the main documentation page](@ref optimization_parameter_fitting) on this topic.
33

44
First, we fetch the required packages.
55
```@example pe_osc_example
@@ -18,18 +18,18 @@ brusselator = @reaction_network begin
1818
B, X --> Y
1919
1, X --> ∅
2020
end
21-
p_real = [:A => 1., :B => 2.]
21+
p_real = [:A => 1.0, :B => 2.0]
2222
nothing # hide
2323
```
2424

2525
We simulate our model, and from the simulation generate sampled data points
2626
(to which we add noise). We will use this data to fit the parameters of our model.
2727
```@example pe_osc_example
2828
u0 = [:X => 1.0, :Y => 1.0]
29-
tspan = (0.0, 30.0)
29+
tend = 30.0
3030
31-
sample_times = range(tspan[1]; stop = tspan[2], length = 100)
32-
prob = ODEProblem(brusselator, u0, tspan, p_real)
31+
sample_times = range(0.0; stop = tend, length = 100)
32+
prob = ODEProblem(brusselator, u0, tend, p_real)
3333
sol_real = solve(prob, Rosenbrock23(); tstops = sample_times)
3434
sample_vals = Array(sol_real(sample_times))
3535
sample_vals .*= (1 .+ .1 * rand(Float64, size(sample_vals)) .- .05)
@@ -48,20 +48,25 @@ scatter!(sample_times, sample_vals'; color = [:blue :red], legend = nothing)
4848
Next, we create a function to fit the parameters using the `ADAM` optimizer. For
4949
a given initial estimate of the parameter values, `pinit`, this function will
5050
fit parameter values, `p`, to our data samples. We use `tend` to indicate the
51-
time interval over which we fit the model.
51+
time interval over which we fit the model. We use an out of place [`set_p` function](@ref simulation_structure_interfacing_functions)
52+
to update the parameter set in each iteration. We also provide the `set_p`, `prob`,
53+
`sample_times`, and `sample_vals` variables as parameters to our optimization problem.
5254
```@example pe_osc_example
53-
function optimise_p(pinit, tend)
54-
function loss(p, _)
55+
set_p = ModelingToolkit.setp_oop(prob, [:A, :B])
56+
function optimize_p(pinit, tend,
57+
set_p = set_p, prob = prob, sample_times = sample_times, sample_vals = sample_vals)
58+
function loss(p, (set_p, prob, sample_times, sample_vals))
59+
p = set_p(prob, p)
5560
newtimes = filter(<=(tend), sample_times)
56-
newprob = remake(prob; tspan = (0.0, tend), p = p)
57-
sol = Array(solve(newprob, Rosenbrock23(); saveat = newtimes))
61+
newprob = remake(prob; p)
62+
sol = Array(solve(newprob, Rosenbrock23(); saveat = newtimes, verbose = false, maxiters = 10000))
5863
loss = sum(abs2, sol .- sample_vals[:, 1:size(sol,2)])
5964
return loss
6065
end
6166
6267
# optimize for the parameters that minimize the loss
6368
optf = OptimizationFunction(loss, Optimization.AutoZygote())
64-
optprob = OptimizationProblem(optf, pinit)
69+
optprob = OptimizationProblem(optf, pinit, (set_p, prob, sample_times, sample_vals))
6570
sol = solve(optprob, ADAM(0.1); maxiters = 100)
6671
6772
# return the parameters we found
@@ -72,45 +77,36 @@ nothing # hide
7277

7378
Next, we will fit a parameter set to the data on the interval `(0, 10)`.
7479
```@example pe_osc_example
75-
p_estimate = optimise_p([5.0, 5.0], 10.0)
80+
p_estimate = optimize_p([5.0, 5.0], 10.0)
7681
```
7782

7883
We can compare this to the real solution, as well as the sample data
7984
```@example pe_osc_example
80-
newprob = remake(prob; tspan = (0., 10.), p = p_estimate)
81-
sol_estimate = solve(newprob, Rosenbrock23())
82-
plot(sol_real; color = [:blue :red], label = ["X real" "Y real"], linealpha = 0.2)
83-
scatter!(sample_times, sample_vals'; color = [:blue :red],
84-
label = ["Samples of X" "Samples of Y"], alpha = 0.4)
85-
plot!(sol_estimate; color = [:darkblue :darkred], linestyle = :dash,
86-
label = ["X estimated" "Y estimated"], xlimit = tspan)
85+
function plot_opt_fit(p, tend)
86+
p = set_p(prob, p)
87+
newprob = remake(prob; tspan = tend, p)
88+
sol_estimate = solve(newprob, Rosenbrock23())
89+
plot(sol_real; color = [:blue :red], label = ["X real" "Y real"], linealpha = 0.2)
90+
scatter!(sample_times, sample_vals'; color = [:blue :red],
91+
label = ["Samples of X" "Samples of Y"], alpha = 0.4)
92+
plot!(sol_estimate; color = [:darkblue :darkred], linestyle = :dash,
93+
label = ["X estimated" "Y estimated"], xlimit = (0.0, tend))
94+
end
95+
plot_opt_fit(p_estimate, 10.0)
8796
```
8897

8998
Next, we use this parameter estimate as the input to the next iteration of our
9099
fitting process, this time on the interval `(0, 20)`.
91100
```@example pe_osc_example
92-
p_estimate = optimise_p(p_estimate, 20.)
93-
newprob = remake(prob; tspan = (0., 20.), p = p_estimate)
94-
sol_estimate = solve(newprob, Rosenbrock23())
95-
plot(sol_real; color = [:blue :red], label = ["X real" "Y real"], linealpha = 0.2)
96-
scatter!(sample_times, sample_vals'; color = [:blue :red],
97-
label = ["Samples of X" "Samples of Y"], alpha = 0.4)
98-
plot!(sol_estimate; color = [:darkblue :darkred], linestyle = :dash,
99-
label = ["X estimated" "Y estimated"], xlimit = tspan)
101+
p_estimate = optimize_p(p_estimate, 20.0)
102+
plot_opt_fit(p_estimate, 20.0)
100103
```
101104

102105
Finally, we use this estimate as the input to fit a parameter set on the full
103106
time interval of the sampled data.
104107
```@example pe_osc_example
105-
p_estimate = optimise_p(p_estimate, 30.0)
106-
107-
newprob = remake(prob; tspan = (0., 30.0), p = p_estimate)
108-
sol_estimate = solve(newprob, Rosenbrock23())
109-
plot(sol_real; color = [:blue :red], label = ["X real" "Y real"], linealpha = 0.2)
110-
scatter!(sample_times, sample_vals'; color = [:blue :red],
111-
label = ["Samples of X" "Samples of Y"], alpha = 0.4)
112-
plot!(sol_estimate; color = [:darkblue :darkred], linestyle = :dash,
113-
label = ["X estimated" "Y estimated"], xlimit = tspan)
108+
p_estimate = optimize_p(p_estimate, 30.0)
109+
plot_opt_fit(p_estimate, 30.0)
114110
```
115111

116112
The final parameter estimate is then
@@ -126,13 +122,6 @@ specifically, we chose our initial interval to be smaller than a full cycle of
126122
the oscillation. If we had chosen to fit a parameter set on the full interval
127123
immediately we would have obtained poor fit and an inaccurate estimate for the parameters.
128124
```@example pe_osc_example
129-
p_estimate = optimise_p([5.0,5.0], 30.0)
130-
131-
newprob = remake(prob; tspan = (0.0,30.0), p = p_estimate)
132-
sol_estimate = solve(newprob, Rosenbrock23())
133-
plot(sol_real; color = [:blue :red], label = ["X real" "Y real"], linealpha = 0.2)
134-
scatter!(sample_times,sample_vals'; color = [:blue :red],
135-
label = ["Samples of X" "Samples of Y"], alpha = 0.4)
136-
plot!(sol_estimate; color = [:darkblue :darkred], linestyle = :dash,
137-
label = ["X estimated" "Y estimated"], xlimit = tspan)
125+
p_estimate = optimize_p([5.0,5.0], 30.0)
126+
plot_opt_fit(p_estimate, 30.0)
138127
```

docs/src/inverse_problems/optimization_ode_param_fitting.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,11 @@ We can now fit our model to data and plot the results:
148148
```@example optimization_paramfit_1
149149
optprob_S_P = OptimizationProblem(objective_function_S_P, p_guess)
150150
optsol_S_P = solve(optprob_S_P, NLopt.LN_NELDERMEAD())
151-
oprob_fitted_S_P = remake(oprob_base; p = optsol_S_P.u)
151+
p = Pair.([:kB, :kD, :kP], optsol_S_P.u)
152+
oprob_fitted_S_P = remake(oprob_base; p)
152153
fitted_sol_S_P = solve(oprob_fitted_S_P)
153-
plot!(fitted_sol_S_P; idxs=[:S, :P], label="Fitted solution", linestyle = :dash, lw = 6, color = [:lightblue :pink])
154-
plot!(plt2, fitted_sol_S_P; idxs=[:S, :P], label="Fitted solution", linestyle = :dash, lw = 6, color = [:lightblue :pink]) # hide
154+
plot!(fitted_sol_S_P; idxs = [:S, :P], label = "Fitted solution", linestyle = :dash, lw = 6, color = [:lightblue :pink])
155+
plot!(plt2, fitted_sol_S_P; idxs = [:S, :P], label = "Fitted solution", linestyle = :dash, lw = 6, color = [:lightblue :pink]) # hide
155156
Catalyst.PNG(plot(plt2; fmt = :png, dpi = 200)) # hide
156157
```
157158

docs/src/model_creation/compositional_modeling.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ reactions *only* within a given system (i.e. ignoring subsystems), we can use
9595
Catalyst.get_species(rn)
9696
```
9797
```@example ex1
98-
ModelingToolkit.get_ps(rn)
98+
Catalyst.get_ps(rn)
9999
```
100100
```@example ex1
101101
Catalyst.get_rxs(rn)
@@ -110,7 +110,6 @@ parameters(rn)
110110
```@example ex1
111111
reactions(rn) # or equations(rn)
112112
```
113-
114113
If we want to collapse `rn` down to a single system with no subsystems we can use
115114
```@example ex1
116115
flatrn = Catalyst.flatten(rn)

0 commit comments

Comments
 (0)