Skip to content

Commit 514598a

Browse files
committed
update CME, SSA monte carlo steady state comp, plot options, axis warn
1 parent f25b626 commit 514598a

File tree

1 file changed

+47
-10
lines changed

1 file changed

+47
-10
lines changed

docs/src/model_simulation/finite_state_projection_simulation.md

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,24 @@
11
# [Solving the chemical master equation using FiniteStateProjection.jl](@id finite-state_projection)
2+
```@raw html
3+
<details><summary><strong>Environment setup and package installation</strong></summary>
4+
```
5+
The following code sets up an environment for running the code on this page.
6+
```julia
7+
using Pkg
8+
Pkg.activate(".")
9+
Pkg.add("Catalyst")
10+
Pkg.add("FiniteStateProjection")
11+
Pkg.add("JumpProcesses")
12+
Pkg.add("OrdinaryDiffEqDefault")
13+
Pkg.add("OrdinaryDiffEqRosenbrock")
14+
Pkg.add("Plots")
15+
Pkg.add("SteadyStateDiffEq")
16+
```
17+
```@raw html
18+
</details>
19+
```
20+
\
21+
222
```@raw html
323
<details><summary><strong>Quick-start example</strong></summary>
424
```
@@ -9,7 +29,7 @@ using Catalyst
929
rs_bistable = @reaction_network begin
1030
    hillr(Y, v, K, n), ∅ --> X
1131
    hillr(X, v, K, n), ∅ --> Y
12-
d, (X,Y) --> 0
32+
d, (X,Y) --> 0
1333
end
1434

1535
# Create a FSPSystem. The second argument denotes species order in u0 and sol.
@@ -39,16 +59,16 @@ As previously discussed, [*stochastic chemical kinetics*](@ref math_models_in_ca
3959

4060
One can study the dynamics of stochastic chemical kinetics models by simulating the stochastic processes using Monte Carlo methods. For example, they can be [exactly sampled](@ref simulation_intro_jumps) using [Stochastic Simulation Algorithms](https://en.wikipedia.org/wiki/Gillespie_algorithm) (SSAs), which are also often referred to as Gillespie's method. To gain a good understanding of a system's dynamics, one typically has to carry out a large number of jump process simulations to minimize sampling error. To avoid such sampling error, an alternative approach is to solve ODEs for the *full probability distribution* that these processes have a given value at each time. Knowing this distribution, one can then calculate any statistic of interest that can be sampled via running many SSA simulations.
4161

42-
[*The chemical master equation*](https://en.wikipedia.org/wiki/Master_equation) (CME) describes the time development of this probability distribution[^1], and is given by a (possibly infinite) coupled system of ODEs (with one ODE for each possible chemical state, i.e. number configuration, of the system). For a system with a single species $X$, the CME looks like
62+
[*The chemical master equation*](https://en.wikipedia.org/wiki/Master_equation) (CME) describes the time development of this probability distribution[^1], and is given by a (possibly infinite) coupled system of ODEs (with one ODE for each possible chemical state, i.e. number configuration, of the system). For a simple [birth-death model](@ref basic_CRN_library_bd) (`(p,d), 0 <--> X`) the CME looks like
4363
```math
4464
\begin{aligned}
45-
\frac{dp(x=0)}{dt} &= f_0(p(x=0), p(x=1), ...) \\
46-
\frac{dp(x=1)}{dt} &= f_0(p(x=0), p(x=1), ...) \\
47-
\frac{dp(x=2)}{dt} &= f_0(p(x=0), p(x=1), ...) \\
65+
\frac{dP(X=0)}{dt} &= d \cdot P(X=1) - p \cdot P(X=0) \\
66+
\frac{dP(X=1)}{dt} &= p \cdot P(X=0) + 2d \cdot P(X=2) - (p + d) P(X=1) \\
4867
&\vdots\\
68+
\frac{dP(X=i)}{dt} &= p \cdot P(X=i-1) + (i + 1)d \cdot P(X=i+1) - (p + i\cdot d) P(X=i)
4969
\end{aligned}
5070
```
51-
For chemical reaction networks in which the total population is bounded, the CME corresponds to a finite set of ODEs. In contrast, for networks in which the system can (in theory) become unbounded, such as networks that include zero order reactions like $\varnothing \to A$, the CME will correspond to an infinite set of ODEs. Even in the finite case, the number of ODEs corresponds to the number of possible state vectors, i.e. vectors with components representing the integer populations of each species in the network. Therefore, for even simple reaction networks there can be many more ODEs than can be represented in typical levels of computer memory, and it becomes infeasible to numerically solve the full system of ODEs that correspond to the CME. However, in many cases the probability of the system attaining species values outside some small range can become negligibly small. Here, a truncated, approximating, version of the CME can be solved practically. An approach for this is the *finite state projection method*[^2]. Below we describe how to use the [FiniteStateProjection.jl](https://github.com/SciML/FiniteStateProjection.jl) package to solve the truncated CME (with the package's [documentation](https://docs.sciml.ai/FiniteStateProjection/dev/) providing a more extensive description). While the CME approach can be very powerful, we note that even for systems with a few species, the truncated CME typically has too many states for it to be feasible to solve the full set of ODEs.
71+
A general form of the CME is provided [here](@ref math_models_in_catalyst_sck_jumps). For chemical reaction networks in which the total population is bounded, the CME corresponds to a finite set of ODEs. In contrast, for networks in which the system can (in theory) become unbounded, such as networks that include zero order reactions like $\varnothing \to A$, the CME will correspond to an infinite set of ODEs. Even in the finite case, the number of ODEs corresponds to the number of possible state vectors, i.e. vectors with components representing the integer populations of each species in the network. Therefore, for even simple reaction networks there can be many more ODEs than can be represented in typical levels of computer memory, and it becomes infeasible to numerically solve the full system of ODEs that correspond to the CME. However, in many cases the probability of the system attaining species values outside some small range can become negligibly small. Here, a truncated, approximating, version of the CME can be solved practically. An approach for this is the *finite state projection method*[^2]. Below we describe how to use the [FiniteStateProjection.jl](https://github.com/SciML/FiniteStateProjection.jl) package to solve the truncated CME (with the package's [documentation](https://docs.sciml.ai/FiniteStateProjection/dev/) providing a more extensive description). While the CME approach can be very powerful, we note that even for systems with a few species, the truncated CME typically has too many states for it to be feasible to solve the full set of ODEs.
5272

5373
## [Finite state projection simulation of single-species model](@id state_projection_one_species)
5474
For this example, we will use a simple [birth-death model](@ref basic_CRN_library_bd), where a single species ($X$) is created and degraded at constant rates ($p$ and $d$, respectively).
@@ -87,7 +107,7 @@ bar(u0, label = "t = 0.0")
87107
We also plot the full distribution using the `bar` function. Finally, the initial condition vector defines the finite space onto which we project the CME. I.e. we will assume that, throughout the entire simulation, the probability of $X$ reaching values outside this initial vector is negligible.
88108

89109
!!! warning
90-
This last bit is important. Even if the probability seems to be very small on the boundary provided by the initial condition, there is still a risk that probability will "leak". Here, it can be good to make simulations using different projections, ensuring that the results are consistent (especially for longer simulations).
110+
This last bit is important. Even if the probability seems to be very small on the boundary provided by the initial condition, there is still a risk that probability will "leak". Here, it can be good to make simulations using different projections, ensuring that the results are consistent (especially for longer simulations). It is also possible to (at any time point) sum up the total probability density to gain a measure of how much has "leaked" (ideally, this number should be as close to 1 as possible).
91111

92112
Now, we can finally create an `ODEProblem` using our `FSPSystem`, initial conditions, and the parameters declared previously. We can simulate this `ODEProblem` like any other ODE.
93113
```@example state_projection_one_species
@@ -102,7 +122,7 @@ bar(osol(1.0);  bar_width = 1.0, linewidth = 0, alpha = 0.7, label = "t = 1.0")
102122
bar!(osol(2.0); bar_width = 1.0, linewidth = 0, alpha = 0.7, label = "t = 2.0")
103123
bar!(osol(5.0); bar_width = 1.0, linewidth = 0, alpha = 0.7, label = "t = 5.0")
104124
bar!(osol(10.0); bar_width = 1.0, linewidth = 0, alpha = 0.7, label = "t = 10.0",
105-
xguide = "X (copy numbers)", yguide = "Probability density")
125+
xguide = "X (copy numbers)", yguide = "Probability density")
106126
```
107127

108128
## [Finite state projection simulation of multi-species model](@id state_projection_multi_species)
@@ -135,18 +155,35 @@ using Plots # hide
135155
using OrdinaryDiffEqRosenbrock
136156
oprob = ODEProblem(fsp_sys, u0, 200.0, ps)
137157
osol = solve(oprob, Rodas5P())
138-
heatmap(osol[end]; xguide = "X₂", yguide = "X")
158+
heatmap(0:24, 0:24, osol[end]; xguide = "X₂", yguide = "X")
139159
```
140160
Here we perform a simulation with a long time span ($t = 200.0$) aiming to find the system's steady state distribution. Next, we plot it using the `heatmap` function.
141161

162+
!!! warning
163+
The `heatmap` function "flips" the plot contrary to what many would consider intuitive. I.e. here the x-axis corresponds to the second species ($X₂$) and the y-axis to the first species ($X$).
164+
142165
## [Finite state projection steady state simulations](@id state_projection_steady_state_sim)
143166
Previously, we have shown how the [SteadyStateDiffEq.jl](https://github.com/SciML/SteadyStateDiffEq.jl) package can be used to [find an ODE's steady state through forward simulation](@ref steady_state_stability). The same interface can be used for ODEs generated through FiniteStateProjection. Below, we use this to find the steady state of the dimerisation example studied in the last example.
144167
```@example state_projection_multi_species
145168
using SteadyStateDiffEq, OrdinaryDiffEqRosenbrock
146169
ssprob = SteadyStateProblem(fsp_sys, u0, ps)
147170
sssol = solve(ssprob, DynamicSS(Rodas5P()))
148-
heatmap(sssol; xguide = "X₂", yguide = "X")
171+
heatmap(0:24, 0:24, sssol; xguide = "X₂", yguide = "X")
172+
```
173+
Finally, we can also approximate this steady state through Monte Carlo jump simulations.
174+
```@example state_projection_multi_species
175+
using JumpProcesses # hide
176+
jprob = JumpProblem(JumpInputs(rs, [:X => 0, :X₂ => 0], (0.0, 100.0), ps))
177+
output_func(sol, i) = (sol[end], false)
178+
eprob = EnsembleProblem(jprob; output_func)
179+
esol = solve(eprob, SSAStepper(); trajectories = 10000)
180+
ss_jump = zeros(25,25)
181+
for endpoint in esol
182+
ss_jump[endpoint[1] + 1, endpoint[2] + 1] += 1
183+
end
184+
heatmap(0:24, 0:24, ss_jump ./length(esol); xguide = "X₂", yguide = "X")
149185
```
186+
Here we used an ensemble [output function]() to only save the final state of each simulation (and then plot these using `heatmap`).
150187

151188

152189
---

0 commit comments

Comments
 (0)