Skip to content

Commit 773d5f6

Browse files
Merge pull request #720 from SciML/NonlinearSolve_tutorial
Tutorial on how to find system steady states usings NonlinearSolve.jl
2 parents 2ccb636 + e97e0d5 commit 773d5f6

File tree

6 files changed

+108
-3
lines changed

6 files changed

+108
-3
lines changed

docs/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
2020
SciMLSensitivity = "1ed8b502-d754-442c-8d5d-10ac956f44a1"
2121
Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46"
2222
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
23+
SteadyStateDiffEq = "9672c7b4-1e72-59bd-8a11-6ac3964bc41f"
2324
StochasticDiffEq = "789caeaf-c7a9-5a7d-9973-96adeb23e2a0"
2425
Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"
2526

docs/pages.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pages = Any["Home" => "index.md",
1313
"Catalyst Applications" => Any["catalyst_applications/simulation_structure_interfacing.md",
1414
"catalyst_applications/advanced_simulations.md",
1515
"catalyst_applications/homotopy_continuation.md",
16+
"catalyst_applications/nonlinear_solve.md",
1617
"catalyst_applications/bifurcation_diagrams.md"],
1718
"Inverse Problems" => Any["inverse_problems/parameter_estimation.md",
1819
"inverse_problems/petab_ode_param_fitting.md"],

docs/src/catalyst_applications/homotopy_continuation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ The order of the species in the output vectors are the same as in `species(wilhe
3939
It should be noted that the steady state multivariate polynomials produced by reaction systems may have both imaginary and negative roots, which are filtered away by `hc_steady_states`. If you want the negative roots, you can use the `hc_steady_states(wilhelm_2009_model, ps; filter_negative=false)` argument.
4040

4141

42-
## Systems with conservation laws
42+
## [Systems with conservation laws](@id homotopy_continuation_conservation_laws)
4343
Some systems are under-determined, and have an infinite number of possible steady states. These are typically systems containing a conservation
4444
law, e.g.
4545
```@example hc3
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# [Finding Steady States using NonlinearSolve.jl](@id nonlinear_solve)
2+
3+
We have previously described how `ReactionSystem` steady states can be found through [homotopy continuation](@ref homotopy_continuation). Catalyst also supports the creation of `NonlinearProblems` corresponding to `ODEProblems` with all left-hand side derivatives set to 0. These can be solved using the variety of nonlinear equation-solving algorithms implemented by [NonlinearSolve.jl](https://github.com/SciML/NonlinearSolve.jl), with the solutions corresponding to system steady states. Generally, using this approach is advantageous when:
4+
- Only a single steady state solution is sought.
5+
- The nonlinear system produced by the model does not correspond to a multivariate, rational, polynomial (homotopy continuation cannot be applied to these systems). Examples include models with non-integer hill coefficients or stoichiometric constants.
6+
7+
However, if all (or multiple) steady states are sought, using homotopy continuation is better.
8+
9+
This tutorial describes how to create `NonlinearProblem`s from Catalyst's `ReactionSystemn`s, and how to solve them using NonlinearSolve. More extensive descriptions of available solvers and options can be found in [NonlinearSolve's documentation](https://docs.sciml.ai/NonlinearSolve/stable/). If you use this in your research, please [cite the NonlinearSolve.jl](@ref nonlinear_solve_citation) and [Catalyst.jl](@ref catalyst_citation) publications.
10+
11+
## Basic example
12+
Let us consider a simple dimerisation network, where a protein ($P$) can exist in a monomer and a dimer form. The protein is produced at a constant rate from its mRNA, which is also produced at a constant rate.
13+
```@example nonlinear_solve1
14+
using Catalyst
15+
dimer_production = @reaction_network begin
16+
pₘ, 0 --> mRNA
17+
pₚ, mRNA --> mRNA + P
18+
(k₁, k₂), 2P <--> P₂
19+
d, (mRNA, P, P₂) --> 0
20+
end
21+
```
22+
This system corresponds to the following ODE:
23+
```math
24+
\begin{aligned}
25+
\frac{dmRNA}{dt} &= pₘ - d \cdot mRNA \\
26+
\frac{dP}{dt} &= pₚ \cdot mRNA - k₁ \cdot P + 2k₂ \cdot P₂ - d \cdot P \\
27+
\frac{dP₂}{dt} &= k₁ \cdot P + 2k₂ \cdot P₂ \\
28+
\end{aligned}
29+
```
30+
To find its steady states we need to solve:
31+
```math
32+
\begin{aligned}
33+
0 &= pₘ - d \cdot mRNA \\
34+
0 &= pₚ \cdot mRNA - k₁ \cdot P + 2k₂ \cdot P₂ - d \cdot P \\
35+
0 &= k₁ \cdot P + 2k₂ \cdot P₂ \\
36+
\end{aligned}
37+
```
38+
39+
To solve this problem, we must first designate our parameter values, and also make an initial guess of the solution. Generally, for problems with a single solution (like this one), most arbitrary guesses will work fine (the exception typically being [systems with conservation laws](@ref nonlinear_solve_conservation_laws)). Using these, we can create the `NonlinearProblem` that we wish to solve.
40+
```@example nonlinear_solve1
41+
p = [:pₘ => 0.5, :pₚ => 2.0, :k₁ => 5.0, :k₂ => 1.0, :d => 1.0]
42+
u_guess = [:mRNA => 1.0, :P => 1.0, :P₂ => 1.0]
43+
nl_prob = NonlinearProblem(dimer_production, u_guess, p)
44+
```
45+
Finally, we can solve it using the `solve` command, returning the steady state solution:
46+
```@example nonlinear_solve1
47+
using NonlinearSolve
48+
sol = solve(nl_prob)
49+
```
50+
51+
NonlinearSolve provides [a wide range of potential solvers](https://docs.sciml.ai/NonlinearSolve/stable/solvers/NonlinearSystemSolvers/). If we wish to designate one, it can be supplied as a second argument to `solve`. Here, we use the Newton Trust Region method, and then check that the solution is equal to the previous one.
52+
```@example nonlinear_solve1
53+
sol_ntr = solve(nl_prob, TrustRegion())
54+
sol ≈ sol_ntr
55+
```
56+
57+
## [Finding steady states through ODE simulations](@id nonlinear_solve_ode_simulation_based)
58+
The `NonlinearProblem`s generated by Catalyst corresponds to ODEs. A common method of solving these is to simulate the ODE from an initial condition until a steady state is reached. NonlinearSolve supports this through the `DynamicSS` method. To use it, an appropriate ODE solver (and any options you wish it to use) must also be supplied (with [a large number being available](https://docs.sciml.ai/DiffEqDocs/stable/solvers/ode_solve/)). Here, we will use the `Tsit5` ODE solver to find the steady states of our dimerisation system.
59+
```@example nonlinear_solve1
60+
using OrdinaryDiffEq, SteadyStateDiffEq
61+
solve(nl_prob, DynamicSS(Tsit5()))
62+
```
63+
Here, we had to import [OrdinaryDiffEq.jl](https://github.com/SciML/OrdinaryDiffEq.jl) (to use `Tsit5`) and [SteadyStateDiffEq.jl](https://github.com/SciML/SteadyStateDiffEq.jl) (to use `DynamicSS`).
64+
65+
Like previously, the found steady state is unaffected by the initial `u` guess. However, when [finding the steady states of systems with conservation laws](@ref nonlinear_solve_conservation_laws) it is important to select a `u` guess that corresponds to the initial condition for the ODE model for which a steady state is sought.
66+
67+
68+
## [Systems with conservation laws](@id nonlinear_solve_conservation_laws)
69+
As described in the section on homotopy continuation, when finding the steady states of systems with conservation laws, [additional considerations have to be taken](homotopy_continuation_conservation_laws). E.g. consider the following two-state system:
70+
```@example nonlinear_solve2
71+
using Catalyst, NonlinearSolve # hide
72+
two_state_model = @reaction_network begin
73+
(k1,k2), X1 <--> X2
74+
end
75+
```
76+
It has an infinite number of steady states. To make steady state finding possible, information of the system's conserved quantities (here $C=X1+X2$) must be provided. Since these can be computed from system initial conditions (`u0`, i.e. those provided when performing ODE simulations), designating an `u0` is often the best way. There are two ways to do this. First, one can perform [ODE simulation-based steady state finding](@ref nonlinear_solve_ode_simulation_based), using the initial condition as the initial `u` guess. Alternatively, any conserved quantities can be eliminated when the `NonlinearProblem` is created. This feature is supported by Catalyst's [conservation law finding and elimination feature](@ref network_analysis_deficiency).
77+
78+
To eliminate conservation laws we simply provide the `remove_conserved = true` argument to `NonlinearProblem`:
79+
```@example nonlinear_solve2
80+
p = [:k1 => 2.0, :k2 => 3.0]
81+
u_guess = [:X1 => 3.0, :X2 => 1.0]
82+
nl_prob = NonlinearProblem(two_state_model, u_guess, p; remove_conserved = true)
83+
nothing # hide
84+
```
85+
here it is important that the quantities used in `u_guess` correspond to the conserved quantities we wish to use. E.g. here the conserved quantity $X1 + X2= 3.0 + 1.0 = 4$ holds for the initial condition, and will hence also hold in the calculated steady-state as well. We can now find the steady states using `solve` like before:
86+
```@example nonlinear_solve2
87+
sol = solve(nl_prob)
88+
```
89+
We note that the output only provides a single value. The reason is that the actual system solved only contains a single equation (the other being eliminated with the conserved quantity). To find the values of $X1$ and $X2$ we can [directly query the solution object for these species' values, using the species themselves as inputs](@ref simulation_structure_interfacing_solutions):
90+
```@example nonlinear_solve2
91+
@unpack X1, X2 = two_state_model
92+
sol[X1]
93+
```
94+
```@example nonlinear_solve2
95+
sol[X2]
96+
```
97+
98+
---
99+
## [Citations](@id nonlinear_solve_citation)
100+
If you use this functionality in your research, [in addition to Catalyst](@ref catalyst_citation), please cite the following papers to support the authors of the NonlinearSolve.jl package:
101+
```
102+
A NonlinearSolve. jl-related publication is in preparation, once it is available, its details will be added here.
103+
```

docs/src/catalyst_applications/simulation_structure_interfacing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ integrator[:X1] = 10.0
8383
Please read [this](@ref advanced_simulations_ssa_callbacks) with regards to updating integrators of `JumpProblem`s.
8484

8585

86-
## Interfacing solution objects
86+
## [Interfacing solution objects](@id simulation_structure_interfacing_solutions)
8787

8888
Finally, we consider solution objects. First, we simulate our problem:
8989
```@example ex1

docs/src/catalyst_functionality/network_analysis.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ and,
273273

274274
![subnetwork_2](../assets/complex_subnets2.svg)
275275

276-
#### Deficiency of the network
276+
#### [Deficiency of the network](@id network_analysis_deficiency)
277277
A famous theorem in Chemical Reaction Network Theory, the Deficiency Zero
278278
Theorem [^1], allows us to use knowledge of the net stoichiometry matrix and the
279279
linkage classes of a *mass action* RRE ODE system to draw conclusions about the

0 commit comments

Comments
 (0)