|
| 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