|
| 1 | +# [Plotting Nullclines and Steady States in Phase Space](@id nullcline_plotting) |
| 2 | + |
| 3 | +Introduction text. |
| 4 | + |
| 5 | +For our example we will use a simple bistable switch model, consisting of two species ($X$ and $Y$) which mutually inhibits each other through repressive hill functions. We will create our model [programmatically](@ref programmatic_CRN_construction). |
| 6 | +```@example nullcline_plotting |
| 7 | +using Catalyst |
| 8 | +t = default_t() |
| 9 | +@parameters v K n |
| 10 | +@species X(t) Y(t) |
| 11 | +rxs = [ |
| 12 | + Reaction(hillr(Y, v, K, n), [], [X]), |
| 13 | + Reaction(hillr(X, v, K, n), [], [Y]), |
| 14 | + Reaction(1.0, [X], []), |
| 15 | + Reaction(1.0, [Y], []), |
| 16 | +] |
| 17 | +@named bs_switch = ReactionSystem(rxs, t) |
| 18 | +bs_switch = complete(bs_switch) |
| 19 | +``` |
| 20 | +Next, we compute the steady states [using homotopy continuation](@ref homotopy_continuation). |
| 21 | +```@example nullcline_plotting |
| 22 | +import HomotopyContinuation |
| 23 | +ps = [v => 1.0, K => 0.6, n => 4.0] |
| 24 | +sss = hc_steady_states(bs_switch, ps; show_progress = false) |
| 25 | +``` |
| 26 | +Next we wish to compute the nullclines. We will first extract the equations corresponding to these from our model. Next, we will compute them using [BifurcationKit.jl](https://github.com/bifurcationkit/BifurcationKit.jl). To generate our equations, we first convert our model to a `NonlinearSystem`. For each nullcline equation, we also have to replace the corresponding variable with a parameter (which can be varied to compute the full nullcline curve). |
| 27 | +```@example nullcline_plotting |
| 28 | +@parameters Xval Yval |
| 29 | +nlsys = convert(NonlinearSystem, bs_switch) |
| 30 | +nc_eq_X = substitute(equations(nlsys)[1], Dict([X => Xval])) |
| 31 | +nc_eq_Y = substitute(equations(nlsys)[2], Dict([Y => Yval])) |
| 32 | +``` |
| 33 | +Next, we compute a `NonlinerSystem` corresponding to each nullcline. |
| 34 | +```@example nullcline_plotting |
| 35 | +@named nc_X_sys = NonlinearSystem([nc_eq_X]) |
| 36 | +@named nc_Y_sys = NonlinearSystem([nc_eq_Y]) |
| 37 | +nc_X_sys = complete(nc_X_sys) |
| 38 | +nc_Y_sys = complete(nc_Y_sys) |
| 39 | +``` |
| 40 | +Finally, for each of these, we can use BifurcationKit's `continuation` function to track the solution of the nullclines as the corresponding variable (the workflow is similar to when we used `bifurcationdiagram` [here](@ref bifurcation_diagrams)). |
| 41 | +```@example nullcline_plotting |
| 42 | +using BifurcationKit |
| 43 | +span = (0.0, 1.2) |
| 44 | +function compute_nullcline(nc_sys, span, var_solve, pvar_bif) |
| 45 | + bprob = BifurcationProblem(nc_sys, [var_solve => 1.0], [ps; pvar_bif => 1.0], pvar_bif; plot_var = var_solve) |
| 46 | + opts_br = ContinuationPar(p_min = span[1], p_max = span[2], dsmax = 0.01) # `dsmax = 0.01` ensures a smooth plot. |
| 47 | + return continuation(bprob, PALC(), opts_br; bothside = true) |
| 48 | +end |
| 49 | +nc_X = compute_nullcline(nc_X_sys, span, Y, Xval) |
| 50 | +nc_Y = compute_nullcline(nc_Y_sys, span, X, Yval) |
| 51 | +nothing # hide |
| 52 | +``` |
| 53 | + |
| 54 | +Finally, we are ready to create our plot. We will plot the steady states using `scatter`. We use the `steady_state_stability` function to [compute steady state stabilities](@ref steady_state_stability) (and use this to determine how to plot the steady state markers). |
| 55 | +```@example nullcline_plotting |
| 56 | +using Plots |
| 57 | +plot(nc_X; vars = (:x, :param), label = "dX/dt = 0", lw = 7, la = 0.7) |
| 58 | +plot!(nc_Y; vars = (:param, :x), label = "dY/dt = 0", lw = 7, la = 0.7) |
| 59 | +for ss in sss |
| 60 | + color, markershape = if steady_state_stability(ss, bs_switch, ps) |
| 61 | + :blue, :circle |
| 62 | + else |
| 63 | + :red, :star4 |
| 64 | + end |
| 65 | + scatter!((ss[2], ss[1]); color, markershape, label = "", markersize = 10) |
| 66 | +end |
| 67 | +scatter!([], []; color = :blue, markershape = :circle, label = "Stable stead state") |
| 68 | +scatter!([], []; color = :red, markershape = :star4, label = "Unstable stead state") |
| 69 | +plot!(xlimit = span, ylimit = span, xguide = "X", yguide = "Y", legendfontsize = 10, size = (500,500)) |
| 70 | +``` |
| 71 | +Here we can see how the steady states occur at the nullclines intersections. |
| 72 | + |
| 73 | +## [Plotting system directions in phase space](@id nullcline_plotting_directions) |
| 74 | + |
| 75 | +```@example nullcline_plotting |
| 76 | +nlprob = NonlinearProblem(complete(nlsys), [X => 0.0, Y => 0.0], ps) |
| 77 | +function get_XY(Xval, Yval) |
| 78 | + prob = Catalyst.remake(nlprob; u0 = [X => Xval, Y => Yval]) |
| 79 | + return prob[equations(nlsys)[2].rhs], prob[equations(nlsys)[1].rhs] |
| 80 | +end |
| 81 | +function plot_xy_arrow!(Xval, Yval) |
| 82 | + dX, dY = get_XY(Xval, Yval) |
| 83 | + dX = dX > 0 ? 0.05 : -0.05 |
| 84 | + dY = dY > 0 ? 0.05 : -0.05 |
| 85 | + plot!([Xval, Xval + dX], [Yval, Yval]; color = :black, lw = 2, arrow = :arrow, label = "") |
| 86 | + plot!([Xval, Xval], [Yval, Yval + dY]; color = :black, lw = 2, arrow = :arrow, label = "") |
| 87 | +end |
| 88 | +arrow_positions = [(0.25, 0.25), (0.75, 0.75), (0.35, 0.8), (0.8, 0.35), (0.02, 1.1), (1.1, 0.02)] |
| 89 | +foreach(pos -> plot_xy_arrow!(pos...), arrow_positions) |
| 90 | +plot!() |
| 91 | +``` |
0 commit comments