Skip to content

Commit 05331ee

Browse files
authored
Merge pull request #1217 from SciML/continuation_of_bifurcation_point_tutorial
Codim 2 bifurcation example
2 parents 4ce70e4 + 2ad1351 commit 05331ee

File tree

3 files changed

+122
-1
lines changed

3 files changed

+122
-1
lines changed

docs/pages.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ pages = Any[
4848
"steady_state_functionality/dynamical_systems.md",
4949
"Examples" => Any[
5050
"steady_state_functionality/examples/bifurcationkit_periodic_orbits.md"
51+
"steady_state_functionality/examples/bifurcationkit_codim2.md"
5152
]
5253
],
5354
"Inverse problems" => Any[

docs/src/steady_state_functionality/bifurcation_diagrams.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Bifurcation diagrams describe how, for a dynamical system, the quantity and type of its steady states change as a parameter is varied[^1]. When using Catalyst-generated models, these can be computed with the [BifurcationKit.jl](https://github.com/bifurcationkit/BifurcationKit.jl) package. Catalyst provides a simple interface for creating BifurcationKit compatible `BifurcationProblem`s from `ReactionSystem`s.
44

5-
This tutorial briefly introduces how to use Catalyst with BifurcationKit through basic examples, with BifurcationKit.jl providing [a more extensive documentation](https://bifurcationkit.github.io/BifurcationKitDocs.jl/stable/). Especially for more complicated systems, where careful tuning of algorithm options might be required, reading the BifurcationKit documentation is recommended. Finally, BifurcationKit provides many additional features not described here, including [computation of periodic orbits](@ref bifurcationkit_periodic_orbits), [tracking of bifurcation points along secondary parameters](https://bifurcationkit.github.io/BifurcationKitDocs.jl/dev/branchswitching/), and [bifurcation computations for PDEs](https://bifurcationkit.github.io/BifurcationKitDocs.jl/dev/tutorials/tutorials/#PDEs:-bifurcations-of-equilibria).
5+
This tutorial briefly introduces how to use Catalyst with BifurcationKit through basic examples, with BifurcationKit.jl providing [a more extensive documentation](https://bifurcationkit.github.io/BifurcationKitDocs.jl/stable/). Especially for more complicated systems, where careful tuning of algorithm options might be required, reading the BifurcationKit documentation is recommended. Finally, BifurcationKit provides many additional features not described here, including [computation of periodic orbits](@ref bifurcationkit_periodic_orbits), [tracking of bifurcation points along secondary parameters](@ref bifurcationkit_codim2), and [bifurcation computations for PDEs](https://bifurcationkit.github.io/BifurcationKitDocs.jl/dev/tutorials/tutorials/#PDEs:-bifurcations-of-equilibria).
66

77
## [Basic example](@id bifurcation_diagrams_basic_example)
88
For this example, we will use a modified version of the model from Wilhelm (2009)[^2] (which
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# [Tracking Bifurcation Point w.r.t. Secondary Parameters using BifurcationKit.jl](@id bifurcationkit_codim2)
2+
Previously, we have shown how to [compute bifurcation diagrams](@ref bifurcation_diagrams) using [BifurcationKit.jl](https://github.com/bifurcationkit/BifurcationKit.jl). In this example, we will show how, after computing the initial diagram, we can track how the position of a bifurcation point moves as a secondary parameter is changed (so-called codimensional 2 bifurcation analysis). More information on how to track bifurcation points along secondary parameters can be found in the [BifurcationKit documentation](https://bifurcationkit.github.io/BifurcationKitDocs.jl/stable/tutorials/ode/tutorialCO/#CO-oxidation-(codim-2)).
3+
4+
## [Computing the bifurcation diagram for the Repressilator](@id bifurcationkit_codim2_bifdia)
5+
We will first compute the bifurcation diagram, using the same approach as in the [corresponding tutorial](@ref bifurcation_diagrams). For this example, we will use the oscillating [Repressilator](@ref basic_CRN_library_repressilator) model.
6+
```@example bifurcationkit_codim2
7+
using Catalyst
8+
repressilator = @reaction_network begin
9+
    hillr(Z,v,K,n), ∅ --> X
10+
    hillr(X,v,K,n), ∅ --> Y
11+
    hillr(Y,v,K,n), ∅ --> Z
12+
d, (X, Y, Z) --> ∅
13+
end
14+
```
15+
Next, we create a `BifurcationProblem` for our model. We will compute the bifurcation diagram with respect to the parameter $v$, and plot the species $X$ in the diagram.
16+
```@example bifurcationkit_codim2
17+
using BifurcationKit
18+
bif_par = :v
19+
u_guess = [:X => 20.0, :Y => 15.0, :Z => 15.0]
20+
p_start = [:v => 10.0, :K => 15.0, :n => 3, :d => 0.2]
21+
plot_var = :X
22+
bprob = BifurcationProblem(repressilator, u_guess, p_start, bif_par; plot_var)
23+
nothing # hide
24+
```
25+
We compute the bifurcation diagram using the `bifurcationdiagram` function. We will compute it across the interval $v \in (0,20)$.
26+
```@example bifurcationkit_codim2
27+
v_span = (0.0, 20.0)
28+
opts_br = ContinuationPar(p_min = v_span[1], p_max = v_span[2])
29+
bifdia = bifurcationdiagram(bprob, PALC(), 3, opts_br; bothside = true)
30+
nothing # hide
31+
```
32+
Finally, we plot the bifurcation diagram.
33+
```@example bifurcationkit_codim2
34+
using Plots
35+
plot(bifdia; xguide = bif_par, yguide = plot_var, branchlabel = "Continuation of steady state w.r.t. v",
36+
linewidthstable = 6, linewidthunstable = 3, markersize = 5)
37+
```
38+
We note that for small values of $v$ the system's single steady state is stable (where the line is thicker). After a [Hopf](https://en.wikipedia.org/wiki/Hopf_bifurcation) bifurcation (the red point), the state turns unstable (where the line is thinner). For chemical reaction networks (which mostly are well-behaved) a single unstable steady state typically corresponds to an oscillation. We can confirm that the system oscillates in the unstable region (while it reaches a stable steady state in the stable region) using simulations:
39+
```@example bifurcationkit_codim2
40+
using OrdinaryDiffEqDefault
41+
p_nosc = [:v => 5.0, :K => 15.0, :n => 3, :d => 0.2]
42+
p_osc = [:v => 15.0, :K => 15.0, :n => 3, :d => 0.2]
43+
prob_nosc = ODEProblem(repressilator, u_guess, 80.0, p_nosc)
44+
prob_osc = ODEProblem(repressilator, u_guess, 80.0, p_osc)
45+
sol_nosc = OrdinaryDiffEqDefault.solve(prob_nosc)
46+
sol_osc = OrdinaryDiffEqDefault.solve(prob_osc)
47+
plot(plot(sol_nosc; title = "v = 5"), plot(sol_osc; title = "v = 15"), size = (1000,400), lw = 4)
48+
```
49+
50+
## [Tracking the bifurcation point w.r.t. a second parameter](@id bifurcationkit_codim2_2ndpar_cont)
51+
Next, we will investigate how the Hopf bifurcation point moves (in $v$-$X$ space) as a second parameter ($K$) is changed. To do this we will use BifurcationKit.jl's [`continuation` function](https://bifurcationkit.github.io/BifurcationKitDocs.jl/dev/library/#BifurcationKit.continuation) (the [`bifurcationdiagram` function](https://bifurcationkit.github.io/BifurcationKitDocs.jl/dev/library/#BifurcationKit.bifurcationdiagram), which we previously have used, works by calling `continuation` recursively). We will call it on the Hopf bifurcation point. First we need to retrieve some indexes that are required to make Catalyst (which primarily indexes through symbols) work with BifurcationKit (which primarily indexes through numbers). A smoother interface for this will hopefully be added in the future.
52+
```@example bifurcationkit_codim2
53+
K_idx = findfirst(isequal(repressilator.K), parameters(complete(convert(NonlinearSystem, repressilator))))
54+
v_idx = findfirst(isequal(repressilator.v), parameters(complete(convert(NonlinearSystem, repressilator))))
55+
K_sym = Symbol(:p, K_idx)
56+
v_sym = Symbol(:p, v_idx)
57+
nothing # hide
58+
```
59+
Next we designate the interval over which we wish to change $K$, and from this create a new `ContinuationPar`.
60+
```@example bifurcationkit_codim2
61+
K_span = (0.01, 27.0)
62+
opts_br_2 = ContinuationPar(p_min = K_span[1], p_max = K_span[2])
63+
nothing # hide
64+
```
65+
Now we can compute the continuation of the Hopf bifurcation. First we must extract a branch from our bifurcation diagram (using [`get_branch`](https://bifurcationkit.github.io/BifurcationKitDocs.jl/stable/library/#BifurcationKit.get_branch)), as `continuation` cannot work on bifurcation diagrams directly (our bifurcation diagram consists of a single branch, which we here extract). Next, we can call `continuation` on it, designating that we wish to perform continuation from the second point on the branch (which corresponds to the Hopf bifurcation, the first point is one of the branch's two endpoints).
66+
```@example bifurcationkit_codim2
67+
branch = get_branch(bifdia, ()).γ
68+
cont_hopf = continuation(branch, 2, (@optic _[K_idx]), opts_br_2; start_with_eigen = true, bothside = true)
69+
nothing # hide
70+
```
71+
We can now plot how the position of the bifurcation point changes with $K$. Here, we use `vars = (v_sym, :x)` to designate that we wish to plot (across the continuation branch) the plotting variable ($X$, which we designated when we created our `BifurcationProblem`) against the first parameter ($v$).
72+
```@example bifurcationkit_codim2
73+
plot(bifdia; branchlabel = "Continuation of steady state w.r.t. v, (K = 15)", linewidthstable = 6,
74+
linewidthunstable = 3, markersize = 5)
75+
plot!(cont_hopf; vars = (v_sym, :x), xlimit = v_span, xguide = bif_par, yguide = plot_var,
76+
branchlabel = "Continuation of Hopf bifurcation w.r.t. K")
77+
```
78+
In this case we cannot see directly which part of the $K$ continuation branch corresponds to low values, however, for low $K$ the Hopf bifurcation occurs for much lower values of $v$ (and corresponds to lower steady state values of $X$). We can check this by e.g. re-computing the Hopf branch for `K_span = (0.01, 20.0)` and see that the rightmost part of the branch is shortened.
79+
80+
We can confirm that the new line corresponds to the Hopf Bifurcation point by recomputing the initial bifurcation diagram, but for a lower $K$ value.
81+
```@example bifurcationkit_codim2
82+
p_start_2 = [:v => 10.0, :K => 10.0, :n => 3, :d => 0.2]
83+
bprob_2 = BifurcationProblem(repressilator, u_guess, p_start_2, bif_par; plot_var)
84+
bifdia_2 = bifurcationdiagram(bprob_2, PALC(), 3, opts_br; bothside = true)
85+
plot!(bifdia_2; xguide = bif_par, yguide = plot_var, branchlabel = "Continuation of steady state w.r.t. v, (K = 10)",
86+
linewidthstable = 6, linewidthunstable = 3, markersize = 5)
87+
```
88+
Here we see that the Hopf bifurcation point of the new diagram also lies on the Hopf continuation line.
89+
90+
Finally, we have already noted that the Hopf bifurcation splits parameter space into one part where the system oscillates and one where it doesn't. Previously we plotted the Hopf continuation in $v$-$X$ space, however, it is also possible to plot it in $v$-$K$ space using the `vars = (v_sym, K_sym)` argument:
91+
```@example bifurcationkit_codim2
92+
xlimit = extrema(getfield.(cont_hopf.branch, v_sym))
93+
ylimit = extrema(getfield.(cont_hopf.branch, K_sym))
94+
plot(cont_hopf; vars = (v_sym, K_sym), xlimit, ylimit, branchlabel = "Hopf bifurcation",
95+
xguide = "v", yguide = "K", lw = 6)
96+
```
97+
Next, we colour parameter space according to whether the steady state is stable (blue) or unstable (red). We also mark two sample values (one in each region).
98+
```@example bifurcationkit_codim2
99+
sample1 = (15.0, 10.0)
100+
sample2 = (5.0, 15.0)
101+
plot(cont_hopf; vars = (v_sym, K_sym), fillrange = ylimit[2], xguide = "v", yguide = "K")
102+
plot!(cont_hopf; vars = (v_sym, K_sym), fillrange = ylimit[1], xlimit, ylimit)
103+
scatter!(sample1; label = "Oscillatory parameter set", markersize = 7)
104+
scatter!(sample2; label = "Non-oscillatory parameter set", markersize = 7)
105+
```
106+
Finally, we can perform one simulation using each of the parameter samples, confirming that one corresponds to an oscillation, while the other one does not.
107+
```@example bifurcationkit_codim2
108+
ps_nosc = [:v => sample2[1], :K => sample2[2], :n => 3, :d => 0.2]
109+
ps_osc = [:v => sample1[1], :K => sample1[2], :n => 3, :d => 0.2]
110+
oprob_nosc = ODEProblem(repressilator, u_guess, 100.0, ps_nosc)
111+
oprob_osc = ODEProblem(repressilator, u_guess, 100.0, ps_osc)
112+
sol_nosc = OrdinaryDiffEqDefault.solve(oprob_nosc)
113+
sol_osc = OrdinaryDiffEqDefault.solve(oprob_osc)
114+
plot(plot(sol_nosc; title = "No oscillation"), plot(sol_osc; title = "Oscillation"); size = (1000, 400), lw = 4)
115+
```
116+
117+
118+
---
119+
## [Citation](@id bifurcationkit_periodic_orbits_citation)
120+
If you use BifurcationKit.jl for your work, we ask that you **cite** the following paper!! Open source development strongly depends on this. It is referenced on [HAL-Inria](https://hal.archives-ouvertes.fr/hal-02902346) with *bibtex* entry [CITATION.bib](https://github.com/bifurcationkit/BifurcationKit.jl/blob/master/CITATION.bib).

0 commit comments

Comments
 (0)