Skip to content

Commit b5edf47

Browse files
authored
Merge pull request #469 from isaacsas/use_symmaps_in_docs
Use symmaps in Problems and explain in the docs
2 parents 19bf353 + ae12d9e commit b5edf47

File tree

12 files changed

+277
-124
lines changed

12 files changed

+277
-124
lines changed

HISTORY.md

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,47 @@
11
# Breaking updates and feature summaries across releases
22

33
## Catalyst unreleased (master branch)
4-
- Added `symmap_to_varmap` and `setdefaults!` to allow setting initial conditions and parameter values using symbols.
4+
5+
## Catalyst 10.4
6+
- Added `symmap_to_varmap`, `setdefaults!`, and updated all `*Problem(rn,...)`
7+
calls to allow setting initial conditions and parameter values using symbol
8+
maps. See the [Catalyst API](https://catalyst.sciml.ai/dev/) for details.
9+
These allow using regular Julia `Symbols` to specify parameter values and
10+
initial conditions. i.e. to set defaults we can do
11+
```julia
12+
rn = @reaction_network begin
13+
α, S + I --> 2I
14+
β, I --> R
15+
end α β
16+
setdefaults!(rn, [:S => 999.0, :I => 1.0, :R => 0.0, => 1e-4, => .01])
17+
op = ODEProblem(rn, [], (0.0,250.0), [])
18+
sol = solve(op, Tsit5())
19+
```
20+
To explicitly pass initial conditions and parameters using symbols we can do
21+
```julia
22+
rn = @reaction_network begin
23+
α, S + I --> 2I
24+
β, I --> R
25+
end α β
26+
u0 = [:S => 999.0, :I => 1.0, :R => 0.0]
27+
p = ( => 1e-4, => .01)
28+
op = ODEProblem(rn, u0, (0.0,250.0), p)
29+
sol = solve(op, Tsit5())
30+
```
31+
In each case ModelingToolkit symbolic variables can be used instead of
32+
`Symbol`s, e.g.
33+
```julia
34+
@parameters α β
35+
@variables t S(t) I(t) R(t)
36+
setdefaults!(rn, [S => 999.0, I => 1.0, R => 0.0, α => 1e-4, β => .01])
37+
```
538

639
## Catalyst 10.3
7-
- **BREAKING:** The order of the parameters in the `ReactionSystem`'s `.ps` field has been changed (only when created through the `@reaction_network` macro). Previously they were ordered according to the order with which they appeared in the macro. Now they are ordered according the to order with which they appeard after the `end` part. E.g. in
40+
- **BREAKING:** The order of the parameters in the `ReactionSystem`'s `.ps`
41+
field has been changed (only when created through the `@reaction_network`
42+
macro). Previously they were ordered according to the order with which they
43+
appeared in the macro. Now they are ordered according the to order with which
44+
they appeard after the `end` part. E.g. in
845
```julia
946
rn = @reaction_network begin
1047
(p,d), 0 <--> X

README.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,17 +85,18 @@ the current master branch.
8585
#### Gillespie Simulations of Michaelis-Menten Enzyme Kinetics
8686

8787
```julia
88+
using Catalyst, Plots, DiffEqJump
8889
rs = @reaction_network begin
8990
c1, S + E --> SE
9091
c2, SE --> S + E
9192
c3, SE --> P + E
9293
end c1 c2 c3
93-
p = (0.00166,0.0001,0.1) # [c1,c2,c3]
94+
p = (:c1 => 0.00166, :c2 => 0.0001, :c3 => 0.1)
9495
tspan = (0., 100.)
95-
u0 = [301., 100., 0., 0.] # [S,E,SE,P]
96+
u0 = [:S => 301, :E => 100, :SE => 0, :P => 0]
9697

9798
# solve JumpProblem
98-
dprob = DiscreteProblem(rs, species(rs) .=> u0, tspan, parameters(rs) .=> p)
99+
dprob = DiscreteProblem(rs, u0, tspan, p)
99100
jprob = JumpProblem(rs, dprob, Direct())
100101
jsol = solve(jprob, SSAStepper())
101102
plot(jsol,lw=2,title="Gillespie: Michaelis-Menten Enzyme Kinetics")
@@ -106,16 +107,16 @@ plot(jsol,lw=2,title="Gillespie: Michaelis-Menten Enzyme Kinetics")
106107
#### Adaptive SDEs for A Birth-Death Process
107108

108109
```julia
109-
using Catalyst, Plots, StochasticDiffEq, DiffEqJump
110+
using Catalyst, Plots, StochasticDiffEq
110111
rs = @reaction_network begin
111112
c1, X --> 2X
112113
c2, X --> 0
113114
c3, 0 --> X
114115
end c1 c2 c3
115-
p = (1.0,2.0,50.) # [c1,c2,c3]
116+
p = (:c1 => 1.0, :c2 => 2.0, :c3 => 50.)
116117
tspan = (0.,10.)
117-
u0 = [5.] # [X]
118-
sprob = SDEProblem(rs, species(rs) .=> u0, tspan, parameters(rs) .=> p)
118+
u0 = [:X => 5.]
119+
sprob = SDEProblem(rs, u0, tspan, p)
119120
ssol = solve(sprob, LambaEM(), reltol=1e-3)
120121
plot(ssol,lw=2,title="Adaptive SDE: Birth-Death Process")
121122
```

docs/src/api/catalyst_api.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,6 @@ reorder_states!
185185
addparam!
186186
addreaction!
187187
setdefaults!
188-
symmap_to_varmap
189188
ModelingToolkit.extend
190189
ModelingToolkit.compose
191190
Catalyst.flatten
@@ -260,3 +259,8 @@ ModelingToolkit.structural_simplify
260259
validate(rx::Reaction; info::String = "")
261260
validate(rs::ReactionSystem, info::String="")
262261
```
262+
263+
## Utility Functions
264+
```@docs
265+
symmap_to_varmap
266+
```

docs/src/faqs.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,67 @@ osys = convert(ODESystem, rn; combinatoric_ratelaws=false)
1313
Disabling these rescalings should work for all conversions of `ReactionSystem`s
1414
to other `ModelingToolkit.AbstractSystem`s.
1515

16+
## How to set default values for initial conditions and parameters?
17+
When directly constructing a `ReactionSystem` these can be passed to the
18+
constructor, and allow solving the system without needing initial condition or
19+
parameter vectors in the generated problem. For example
20+
```julia
21+
using Catalyst, Plots, OrdinaryDiffEq
22+
@parameters β ν
23+
@variables t S(t) I(t) R(t)
24+
rx1 = Reaction(β, [S,I], [I], [1,1], [2])
25+
rx2 = Reaction(ν, [I], [R])
26+
defs ==> 1e-4, ν => .01, S => 999.0, I => 1.0, R => 0.0]
27+
@named sir = ReactionSystem([rx1,rx2],t; defaults=defs)
28+
oprob = ODEProblem(sir, [], (0.0,250.0))
29+
sol = solve(oprob, Tsit5())
30+
plot(sol)
31+
```
32+
alternatively we could also have said
33+
```julia
34+
@parameters β=1e-4 ν=.01
35+
@variables t S(t)=999.0 I(t)=1.0 R(t)=0.0
36+
rx1 = Reaction(β, [S,I], [I], [1,1], [2])
37+
rx2 = Reaction(ν, [I], [R])
38+
@named sir = ReactionSystem([rx1,rx2],t)
39+
```
40+
41+
The `@reaction_network` macro does not currently provide a way to specify
42+
default values, however, they can be added after creating the system via the
43+
`setdefaults!` command, like
44+
```julia
45+
sir = @reaction_network sir begin
46+
β, S + I --> 2I
47+
ν, I --> R
48+
end β ν
49+
setdefaults!(sir, [ => 1e-4, => .01, :S => 999.0, :I => 1.0, :R => 0.0])
50+
```
51+
52+
## How to specify initial conditions and parameters values for `ODEProblem` and other problem types?
53+
To explicitly pass initial conditions and parameters we can use mappings from
54+
Julia `Symbol`s corresponding to each variable/parameter to values, or from
55+
ModelingToolkit symbolic variables to each variable/parameter. Using `Symbol`s
56+
we have
57+
```julia
58+
rn = @reaction_network begin
59+
α, S + I --> 2I
60+
β, I --> R
61+
end α β
62+
u0 = [:S => 999.0, :I => 1.0, :R => 0.0]
63+
p = ( => 1e-4, => .01)
64+
op = ODEProblem(rn, u0, (0.0,250.0), p)
65+
sol = solve(op, Tsit5())
66+
```
67+
while using ModelingToolkit symbolic variables we have
68+
```julia
69+
@parameters α β
70+
@variables t S(t) I(t) R(t)
71+
u0 = [S => 999.0, I => 1.0, R => 0.0]
72+
p ==> 1e-4, β => .01)
73+
op = ODEProblem(rn, u0, (0.0,250.0), p)
74+
sol = solve(op, Tsit5())
75+
```
76+
1677
## How to modify generated ODEs?
1778
Conversion to other `ModelingToolkit.AbstractSystem`s allows the possibility to
1879
modify the system with further terms that are difficult to encode as a chemical

docs/src/index.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,11 @@ which in Jupyter notebooks will give the figure
8080

8181
To generate and solve a mass action ODE version of the model we use
8282
```julia
83-
using DiffEqBase, OrdinaryDiffEq
84-
p = [.1/1000, .01] # [α,β]
83+
using OrdinaryDiffEq
84+
p = [ => .1/1000, => .01]
8585
tspan = (0.0,250.0)
86-
u0 = [999.0,1.0,0.0] # [S,I,R] at t=0
87-
op = ODEProblem(rn, species(rn) .=> u0, tspan, parameters(rn) .=> p)
86+
u0 = [:S => 999.0, :I => 1.0, :R => 0.0]
87+
op = ODEProblem(rn, u0, tspan, p)
8888
sol = solve(op, Tsit5()) # use Tsit5 ODE solver
8989
```
9090
which we can plot as

docs/src/tutorials/advanced_examples.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,21 @@ Jacobians with or without threading and/or parallelization), creating LaTeX
88
representations for systems, etc.
99

1010
Note, when generating problems from other system types, `u0` and `p` must
11-
provide vectors of `Pair`s that map each species or parameter to their numerical
12-
value. E.g., for the Michaelis-Menten example above we'd use
11+
provide vectors, tuples or dictionaries of `Pair`s that map each the symbolic
12+
variables for each species or parameter to their numerical value. E.g., for the
13+
Michaelis-Menten example above we'd use
1314
```julia
1415
rs = @reaction_network begin
1516
c1, X --> 2X
1617
c2, X --> 0
1718
c3, 0 --> X
1819
end c1 c2 c3
19-
p = (1.0,2.0,50.)
20+
p = (:c1 => 1.0, :c2 => 2.0, :c3 => 50.)
21+
pmap = symmap_to_varmap(rs,p) # convert Symbol map to symbolic variable map
2022
tspan = (0.,4.)
21-
u0 = [5.]
23+
u0 = [:X => 5.]
24+
u0map = symmap_to_varmap(rs,u0) # convert Symbol map to symbolic variable map
2225
osys = convert(ODESystem, rs)
23-
u0map = map((x,y) -> Pair(x,y), species(rs), u0)
24-
pmap = map((x,y) -> Pair(x,y), parameters(rs), p)
2526
oprob = ODEProblem(osys, u0map, tspan, pmap)
2627
sol = solve(oprob, Tsit5())
2728
```

docs/src/tutorials/basic_examples.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ rs = @reaction_network begin
88
c2, X --> 0
99
c3, 0 --> X
1010
end c1 c2 c3
11-
p = (1.0,2.0,50.) # [c1,c2,c3]
11+
p = (:c1 => 1.0, :c2 => 2.0, :c3 => 50.)
1212
tspan = (0.,4.)
13-
u0 = [5.] # [X]
13+
u0 = [:X => 5.]
1414

1515
# solve ODEs
1616
oprob = ODEProblem(rs, u0, tspan, p)
@@ -25,7 +25,7 @@ sprob = SDEProblem(rs, u0, tspan, p)
2525
ssol = solve(sprob, EM(), dt=.01)
2626

2727
# solve JumpProblem
28-
u0 = [5]
28+
u0 = [:X => 5]
2929
dprob = DiscreteProblem(rs, u0, tspan, p)
3030
jprob = JumpProblem(rs, dprob, Direct())
3131
jsol = solve(jprob, SSAStepper())
@@ -39,16 +39,16 @@ rs = @reaction_network begin
3939
c2, SE --> S + E
4040
c3, SE --> P + E
4141
end c1 c2 c3
42-
p = (0.00166,0.0001,0.1) # [c1,c2,c3]
42+
p = (:c1 => 0.00166, :c2 => 0.0001, :c3 => 0.1)
4343
tspan = (0., 100.)
44-
u0 = [301., 100., 0., 0.] # [S,E,SE,P]
44+
u0 = [:S => 301., :E => 100., :SE => 0., :P => 0.]
4545

4646
# solve ODEs
4747
oprob = ODEProblem(rs, u0, tspan, p)
4848
osol = solve(oprob, Tsit5())
4949

5050
# solve JumpProblem
51-
u0 = [301, 100, 0, 0]
51+
u0 = [:S => 301, :E => 100, :SE => 0, :P => 0]
5252
dprob = DiscreteProblem(rs, u0, tspan, p)
5353
jprob = JumpProblem(rs, dprob, Direct())
5454
jsol = solve(jprob, SSAStepper())

docs/src/tutorials/dsl.md

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,42 @@ these are the same as for normal variables in Julia.
3535

3636
The chemical reaction model is generated by the `@reaction_network` macro and
3737
stored in the `rn` variable (a normal Julia variable, which does not need to be
38-
called `rn`). The generated `ReactionSystem` can be converted to a differential
39-
equation model via
38+
called `rn`). It corresponds to a [`ReactionSystem`](@ref), a symbolic
39+
representation of the chemical network. The generated `ReactionSystem` can be
40+
converted to a symbolic differential equation model via
4041
```julia
4142
osys = convert(ODESystem, rn)
42-
oprob = ODEProblem(osys, species(rn) .=> u0, tspan, parameters(rn) .=> p)
4343
```
44-
or more directly via
44+
45+
We can then convert the symbolic ODE model into a compiled, optimized
46+
representation for use in the SciML ODE solvers by constructing an `ODEProblem`.
47+
Creating an `ODEProblem` also requires our specifying the initial conditions for
48+
the model. We do this by creating a mapping from each symbolic variable
49+
representing a chemical species to its initial value
50+
```julia
51+
# define the symbolic variables
52+
@variables t, X(t), Y(t), Z(t), XY(t), Z1(t), Z2(t)
53+
54+
# create the mapping
55+
u0 = [X => 1.0, Y => 1.0, XY => 1.0, Z1 => 1.0, Z2 => 1.0]
56+
```
57+
Alternatively, we can create a mapping use Julia `Symbol`s for each variable, and then convert them to a mapping involving symbolic variables like
58+
```julia
59+
u0 = symmap_to_varmap(rn, [:X => 1.0, :Y => 1.0, :XY => 1.0, :Z1 => 1.0, :Z2 => 1.0])
60+
```
61+
Given the mapping, we can then create an `ODEProblem` from our symbolic `ODESystem`
4562
```julia
46-
oprob = ODEProblem(rn, species(rn) .=> u0, tspan, parameters(rn) .=> p)
63+
oprob = ODEProblem(osys, u0, tspan, [])
4764
```
65+
66+
Catalyst provides a shortcut to avoid having to explicitly `convert` to an
67+
`ODESystem` and/or use `symmap_to_varmap`, allowing direct construction
68+
of the `ODEProblem` like
69+
```julia
70+
u0 = [:X => 1.0, :Y => 1.0, :XY => 1.0, :Z1 => 1.0, :Z2 => 1.0]
71+
oprob = ODEProblem(rn, u0, tspan, [])
72+
```
73+
4874
For more detailed examples, see the [Basic Chemical Reaction Network
4975
Examples](@ref). The generated differential equations use the law of mass
5076
action. For the above example, the ODEs are then

0 commit comments

Comments
 (0)