Skip to content

Commit dc04a1b

Browse files
authored
Merge pull request #764 from SciML/dynamical_systems_tutorial
[v14 ready] Tutorial on how to use DynamicalSystems.jl for Catalyst
2 parents def0381 + 83ef0f6 commit dc04a1b

File tree

4 files changed

+160
-4
lines changed

4 files changed

+160
-4
lines changed

docs/Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
[deps]
22
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
33
BifurcationKit = "0f109fa4-8a5d-4b75-95aa-f515264e7665"
4+
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
45
Catalyst = "479239e8-5488-4da2-87a7-35f2df7eef83"
56
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
67
DiffEqParamEstim = "1130ab10-4a5a-5621-a13d-e4788d82bd4c"
78
DifferentialEquations = "0c46a032-eb83-5123-abaf-570d42b7fbaa"
89
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
910
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
10-
GlobalSensitivity = "af5da776-676b-467e-8baf-acd8249e4f0f"
11+
DynamicalSystems = "61744808-ddfa-5f27-97ff-6e42cc95d634"
12+
GlobalSensitivity = "af5da776-676b-467e-8baf-acd8249e4f0f"#
1113
HomotopyContinuation = "f213a82b-91d6-5c5d-acf7-10f1c761b327"
1214
IncompleteLU = "40713840-3770-5561-ab4c-a76e7d0d7895"
1315
JumpProcesses = "ccbc3e58-028d-4f4c-8cd5-9ae44345cda5"

docs/pages.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ pages = Any[
4141
"steady_state_functionality/homotopy_continuation.md",
4242
"steady_state_functionality/nonlinear_solve.md",
4343
"steady_state_functionality/steady_state_stability_computation.md",
44-
"steady_state_functionality/bifurcation_diagrams.md"
45-
# Dynamical systems analysis.
44+
"steady_state_functionality/bifurcation_diagrams.md",
45+
"steady_state_functionality/dynamical_systems.md"
4646
],
4747
"Inverse Problems" => Any[
4848
# Inverse problems introduction.
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# [Analysing model steady state properties with DynamicalSystems.jl](@id dynamical_systems)
2+
The [DynamicalSystems.jl package](https://github.com/JuliaDynamics/DynamicalSystems.jl) implements a wide range of methods for analysing dynamical systems. This includes both continuous-time systems (i.e. ODEs) and discrete-times ones (difference equations, however, these are not relevant to chemical reaction network modelling). Here we give two examples of how DynamicalSystems.jl can be used, with the package's [documentation describing many more features](https://juliadynamics.github.io/DynamicalSystemsDocs.jl/dynamicalsystems/dev/tutorial/). Finally, it should also be noted that DynamicalSystems.jl contain several tools for [analysing data measured from dynamical systems](https://juliadynamics.github.io/DynamicalSystemsDocs.jl/dynamicalsystems/dev/contents/#Exported-submodules).
3+
4+
## [Finding basins of attraction](@id dynamical_systems_basins_of_attraction)
5+
Given enough time, an ODE will eventually reach a so-called [*attractor*](https://en.wikipedia.org/wiki/Attractor). For chemical reaction networks (CRNs), this will typically be either a *steady state* or a *limit cycle*. Since ODEs are deterministic, which attractor a simulation will reach is uniquely determined by the initial condition (assuming parameter values are fixed). Conversely, each attractor is associated with a set of initial conditions such that model simulations originating in these will tend to that attractor. These sets are called *basins of attraction*. Here, phase space (the space of all possible states of the system) can be divided into a number of basins of attraction equal to the number of attractors.
6+
7+
DynamicalSystems.jl provides a simple interface for finding an ODE's basins of attraction across any given subspace of phase space. In this example we will use the bistable [Wilhelm model](https://bmcsystbiol.biomedcentral.com/articles/10.1186/1752-0509-3-90) (which steady states we have previous [computed using homotopy continuation](@ref homotopy_continuation_basic_example)). As a first step, we create an `ODEProblem` corresponding to the model which basins of attraction we wish to compute. For this application, `u0` and `tspan` is unused, and their values are of little importance (the only exception is than `tspan`, for implementation reason, must provide a not too small interval, we recommend minimum `(0.0, 1.0)`).
8+
```@example dynamical_systems_basins
9+
using Catalyst
10+
wilhelm_model = @reaction_network begin
11+
k1, Y --> 2X
12+
k2, 2X --> X + Y
13+
k3, X + Y --> Y
14+
k4, X --> 0
15+
end
16+
17+
u0 = [:X => 0.0, :Y => .0]
18+
tspan = (0.0, 10.0)
19+
ps = [:k1 => 8.0, :k2 => 2.0, :k3 => 1.0, :k4 => 1.5]
20+
oprob = ODEProblem(wilhelm_model, u0, tspan, ps)
21+
nothing # hide
22+
```
23+
Next, for any application of DynamicalSystems.jl, our `ODEProblem` must be converted into a so-called `CoupledODEs` structure. This is done by combining the ODE with the solver (and potential solver options) with which we wish to simulate it (just like when it is simulated using `solve`). Here, we will simply designate the `Tsit5` numeric solver (but provide no other options).
24+
```@example dynamical_systems_basins
25+
using DynamicalSystems, OrdinaryDiffEq
26+
ds = CoupledODEs(oprob, (alg = Tsit5(),))
27+
```
28+
We can now compute the basins of attraction. This is done by first creating a grid that designates which subspace of phase-space we wish to investigate (here, the corresponding basin of attraction is found for every point on the grid). Next, we create a `AttractorsViaRecurrences` struct, that maps initial conditions to attractors, and then use that as input to the `basins_of_attraction` function.
29+
```@example dynamical_systems_basins
30+
# We provide one grid of values for each species. These are then bundled into a tuple.
31+
x_grid = 0.0:0.03:6.0
32+
y_grid = 0.0:0.03:9.0
33+
grid = (x_grid, y_grid)
34+
avr = AttractorsViaRecurrences(ds, grid)
35+
basins, attractors = basins_of_attraction(avr, grid; show_progress = false)
36+
attractors
37+
```
38+
Here, `attractors` is a dictionary that maps attractor labels (integers) to attractors. In this case we have two fixed points, one at $(0.0,0.0)$ and one at $(4.5,6.0)$. Next, `basins` is a matrix of equal size to `grid`, where each value is an integer describing to which attractor's basin that state belongs.
39+
40+
DynamicalSystems.jl also provides a simple interface for plotting the resulting basins. This uses [Makie.jl](https://docs.makie.org/stable/), an alternative plotting package to [Plots.jl](https://github.com/JuliaPlots/Plots.jl) (which is typically the preferred plotting package within the context of Catalyst). Generally, Makie is good at creating animations or interactive graphics (however, it is also a [popular competitor to Plots.jl for general-purpose plotting](https://juliapackagecomparisons.github.io/pages/plotting/)).
41+
```@example dynamical_systems_basins
42+
using CairoMakie
43+
heatmap_basins_attractors(grid, basins, attractors)
44+
```
45+
Here, in addition to the basins of attraction, the system's three steady states are marked (the one at the intersection of the two basins is unstable).
46+
47+
!!! warning
48+
Both Makie and Plots.jl exports a function called `plot`. Hence, if both these packages are imported into the same session, calls to `plot` must be prepended with the package one wishes to use (e.g. `Plots.plot(sol)`).
49+
50+
More information on how to compute basins of attractions for ODEs using DynamicalSystems.jl can be found [here](https://juliadynamics.github.io/DynamicalSystemsDocs.jl/attractors/stable/basins/).
51+
52+
## [Computing Lyapunov exponents](@id dynamical_systems_lyapunov_exponents)
53+
[*Lyapunov exponents*](https://en.wikipedia.org/wiki/Lyapunov_exponent) are scalar values that can be computed for any attractor of an ODE. For an ODE with $n$ variables, for each state, a total of $n$ Lyapunov exponents can be computed (and these are collectively called the *Lyapunov spectrum*). Positive Lyapunov exponents indicate that trajectories initially infinitesimally close diverge from each other. Conversely, negative Lyapunov exponents suggests that trajectories converge to each others.
54+
55+
While Lyapunov exponents can be used for other purposes, they are primarily used to characterise [*chaotic behaviours*](https://en.wikipedia.org/wiki/Chaos_theory) (where small changes in initial conditions has large effect on the resulting trajectories). Generally, an ODE exhibit chaotic behaviour if its attractor(s) have *at least one* positive Lyapunov exponent. Practically, Lyapunov exponents can be computed using DynamicalSystems.jl's `lyapunovspectrum` function. Here we will use it to investigate two models, one which exhibits chaos and one which do not.
56+
57+
First, let us consider the [Willamowski–Rössler model](@ref ref), which is known to exhibit chaotic behaviour.
58+
```@example dynamical_systems_lyapunov
59+
using Catalyst
60+
wr_model = @reaction_network begin
61+
k1, 2X --> 3X
62+
k2, X --> 2X
63+
k3, Z + 2X --> 2Z
64+
k4, Y + X --> 2Y
65+
k5, Y --> ∅
66+
k6, 2Z --> ∅
67+
k7, Z --> ∅
68+
end
69+
```
70+
We can simulate the model, noting that its behaviour seems chaotic.
71+
```@example dynamical_systems_lyapunov
72+
using OrdinaryDiffEq, Plots
73+
74+
u0 = [:X => 1.5, :Y => 1.5, :Z => 1.5]
75+
tspan = (0.0, 100.0)
76+
p = [:k1 => 2.1, :k2 => 0.7, :k3 => 2.9, :k4 => 1.1, :k5 => 1.0, :k6 => 0.5, :k7 => 2.7]
77+
78+
oprob = ODEProblem(wr_model, u0, tspan, p)
79+
sol = solve(oprob, Rodas5P())
80+
plot(sol; idxs=(:X, :Y, :Z))
81+
```
82+
Next, like when we [computed basins of attraction](@ref dynamical_systems_basins_of_attraction), we create a `CoupledODEs` corresponding to the model and state for which we wish to compute our Lyapunov spectrum. Lke previously, `tspan` must provide some small interval (at least `(0.0, 1.0)` is recommended), but else have no impact on the computed Lyapunov spectrum.
83+
```@example dynamical_systems_lyapunov
84+
using DynamicalSystems
85+
ds = CoupledODEs(oprob, (alg = Rodas5P(autodiff = false),))
86+
nothing # hide
87+
```
88+
Here, the `autodiff = false` argument is required when Lyapunov spectrums are computed. We can now provide our `CoupledODEs` (`ds`) to `lyapunovspectrum` to compute the lyapunov spectrum. This function requires a second argument (here set to `100`). Generally setting this to a higher value will increase accuracy, but also increase runtime (since `lyapunovspectrum` is fast for most systems, setting this to a large value is recommended).
89+
```@example dynamical_systems_lyapunov
90+
lyapunovspectrum(ds, 100)
91+
```
92+
Here, the largest exponent is positive, suggesting that the model is chaotic (or, more accurately, it has at least one chaotic attractor, to which is approached from the initial condition $(1.5,1.5,1.5)).
93+
94+
Next, we consider the [Brusselator] model. First we simulate the model for two similar initial conditions, confirming that they converge to the same limit cycle:
95+
```@example dynamical_systems_lyapunov
96+
brusselator = @reaction_network begin
97+
A, ∅ --> X
98+
1, 2X + Y --> 3X
99+
B, X --> Y
100+
1, X --> ∅
101+
end
102+
103+
u0_1 = [:X => 1.0, :Y => 1.0]
104+
u0_2 = [:X => 1.2, :Y => 1.0]
105+
tspan = (0., 25.)
106+
ps = [:A => 1.0, :B => 4.0]
107+
108+
oprob1 = ODEProblem(brusselator, u0_1, tspan, ps)
109+
oprob2 = ODEProblem(brusselator, u0_2, tspan, ps)
110+
osol1 = solve(oprob1, Tsit5())
111+
osol2 = solve(oprob2, Tsit5())
112+
plot(osol1; idxs = (:X, :Y))
113+
plot!(osol2; idxs = (:X, :Y))
114+
```
115+
Next, we compute the Lyapunov spectrum at one of the initial conditions:
116+
```@example dynamical_systems_lyapunov
117+
ds = CoupledODEs(oprob1, (alg = Rodas5P(autodiff = false),))
118+
lyapunovspectrum(ds, 100)
119+
```
120+
Here, all Lyapunov exponents are negative, confirming that the brusselator is non-chaotic.
121+
122+
More details on how to compute Lyapunov exponents using DynamicalSystems.jl can be found [here](https://juliadynamics.github.io/ChaosTools.jl/stable/lyapunovs/). A full overview of tools for analysing chaotic behaviours (using the "ChaosTools.jl subpackage) can be found [here](https://juliadynamics.github.io/ChaosTools.jl/stable/).
123+
124+
125+
---
126+
## [Citations](@id dynamical_systems_citations)
127+
If you use this functionality in your research, [in addition to Catalyst](@ref catalyst_citation), please cite the following paper to support the author of the DynamicalSystems.jl package:
128+
```
129+
@article{DynamicalSystems.jl-2018,
130+
doi = {10.21105/joss.00598},
131+
url = {https://doi.org/10.21105/joss.00598},
132+
year = {2018},
133+
month = {mar},
134+
volume = {3},
135+
number = {23},
136+
pages = {598},
137+
author = {George Datseris},
138+
title = {DynamicalSystems.jl: A Julia software library for chaos and nonlinear dynamics},
139+
journal = {Journal of Open Source Software}
140+
}
141+
```
142+
143+
144+
---
145+
## Learning more
146+
147+
If you want to learn more about analysing dynamical systems, including chaotic behaviour, you can have a look at the textbook [Nonlinear Dynamics](https://link.springer.com/book/10.1007/978-3-030-91032-7). It utilizes DynamicalSystems.jl and provides a concise, hands-on approach to learning nonlinear dynamics and analysing dynamical systems [^1].
148+
149+
150+
---
151+
## References
152+
[^1]: [G. Datseris, U. Parlitz, *Nonlinear dynamics: A concise introduction interlaced with code*, Springer (2022).](https://link.springer.com/book/10.1007/978-3-030-91032-7)
153+
[^2]: [S. H. Strogatz, *Nonlinear Dynamics and Chaos*, Westview Press (1994).](http://users.uoa.gr/~pjioannou/nonlin/Strogatz,%20S.%20H.%20-%20Nonlinear%20Dynamics%20And%20Chaos.pdf)
154+
[^3]: [A. M. Lyapunov, *The general problem of the stability of motion*, International Journal of Control (1992).](https://www.tandfonline.com/doi/abs/10.1080/00207179208934253)

docs/src/steady_state_functionality/homotopy_continuation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ integer Hill exponents). The roots of these can reliably be found through a
1111

1212
Catalyst contains a special homotopy continuation extension that is loaded whenever HomotopyContinuation.jl is. This exports a single function, `hc_steady_states`, that can be used to find the steady states of any `ReactionSystem` structure.
1313

14-
## Basic example
14+
1515
For this tutorial, we will use the [Wilhelm model](@ref ref) (which
1616
demonstrates bistability in a small chemical reaction network). We declare the
1717
model and the parameter set for which we want to find the steady states:

0 commit comments

Comments
 (0)