Skip to content

Commit 41d1ef0

Browse files
fix docs
1 parent ce837e4 commit 41d1ef0

7 files changed

Lines changed: 146 additions & 9 deletions

File tree

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "AlgebraicAgents"
22
uuid = "f6eb0ae3-10fa-40e6-88dd-9006ba45093a"
3-
version = "0.3.20"
3+
version = "0.3.21"
44

55
[deps]
66
Crayons = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f"

docs/make.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const generated_dir = joinpath(@__DIR__, "src", "sketches")
3636
const skip_dirs = ["traces"]
3737

3838
for (root, dirs, files) in walkdir(literate_dir)
39-
if any(occursin.(skip_dirs, root))
39+
if any(occursin.(skip_dirs, root)) || startswith(root, "_")
4040
continue
4141
end
4242
out_dir = joinpath(generated_dir, relpath(root, literate_dir))

docs/src/sketches/agents/agents.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
```@meta
2-
EditURL = "<unknown>/../tutorials/agents/agents.jl"
2+
EditURL = "../../../../tutorials/agents/agents.jl"
33
```
44

55
# Agents.jl Integration

docs/src/sketches/algebraicdynamics/algebraicdynamics.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
```@meta
2-
EditURL = "<unknown>/../tutorials/algebraicdynamics/algebraicdynamics.jl"
2+
EditURL = "../../../../tutorials/algebraicdynamics/algebraicdynamics.jl"
33
```
44

55
# Lotka-Voltera Two Ways

docs/src/sketches/molecules/molecules.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
```@meta
2-
EditURL = "<unknown>/../tutorials/molecules/molecules.jl"
2+
EditURL = "../../../../tutorials/molecules/molecules.jl"
33
```
44

55
# A Toy Pharma Model

docs/src/sketches/sciml/sciml.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
```@meta
2-
EditURL = "<unknown>/../tutorials/sciml/sciml.jl"
2+
EditURL = "../../../../tutorials/sciml/sciml.jl"
33
```
44

55
# SciML Integration

docs/src/sketches/stochastic_simulation/anderson.md

Lines changed: 140 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
```@meta
2-
EditURL = "<unknown>/../tutorials/stochastic_simulation/anderson.jl"
2+
EditURL = "../../../../tutorials/stochastic_simulation/anderson.jl"
33
```
44

55
# Simulating Stochastic Reaction Systems
@@ -217,7 +217,7 @@ Let us use parameters $\beta$ to represent the effective contact rate, and
217217
$\gamma$ to represent the recovery rate.
218218

219219
````@example anderson
220-
β = 0.05*10.0
220+
β = 0.05*10.0/1000
221221
γ = 0.25
222222
````
223223

@@ -228,7 +228,7 @@ to the rates given by the anonymous functions passed to `add_clock!`.
228228

229229
````@example anderson
230230
rs = make_reactionsystem("SIR", [990, 10, 0])
231-
add_clock!(rs, "infection", (x) -> β*x[2]/sum(x)*x[1], [-1,1,0])
231+
add_clock!(rs, "infection", (x) -> β*x[2]*x[1], [-1,1,0])
232232
add_clock!(rs, "recovery", (x) -> γ*x[2], [0,-1,1])
233233
````
234234

@@ -250,3 +250,140 @@ df_out = select(rs.df_output, Not(:clock));
250250
plot(df_out[!,:time], Matrix(df_out[:,[:X1,:X2,:X3]]), label = ["S" "I" "R"])
251251
````
252252

253+
## Stochastic Petri Net
254+
255+
Stochastic Petri nets (SPN) are a mathematical language to describe distributed systems which evolve according
256+
to a stochastic trajectory. There are many ways to define them, and for a comprehensive overview of their modeling
257+
power, we reccomend [Haas (2002)](https://link.springer.com/book/10.1007/b97265). We will implement
258+
a very simple SPN to set up a state transition system. Our SPN is nearly identical to the category
259+
of Petri net proposed by [Kock (2023)](https://arxiv.org/abs/2005.05108), with the addition of a rate
260+
parameter associated with each transition. When we assume that overall transition rates occur according to the
261+
mass action law multiplied by the rate constant associated with that transition, we will be able to
262+
produce a `ReactionSystem` that can be simulated using the code above.
263+
264+
````@example anderson
265+
@aagent struct StochasticPetriNet
266+
P::Vector{Symbol}
267+
T::Vector{Symbol}
268+
I::Int
269+
O::Int
270+
271+
ip::Vector{Symbol}
272+
it::Vector{Symbol}
273+
op::Vector{Symbol}
274+
ot::Vector{Symbol}
275+
276+
rate::Dict{Symbol,Float64}
277+
end
278+
````
279+
280+
The `StochasticPetriNet` has objects corresponding to (Sets) of places, transitions, input and output arcs. There
281+
are mappings (Functions) which indicate which place or transition each input (output) arc is connected to. For example
282+
`ip` is of length `I`, such that each input arc identifies which place is is connected to (likewise for `it`, but for
283+
transitions). Instead of arc multiplicites, we duplicate arcs, which has the same effect, and simplifies the code.
284+
285+
We write a helper function to construct SPNs. It is only responsible for checking our input makes sense.
286+
287+
````@example anderson
288+
function make_stochasticpetrinet(name, P, T, I, O, ip, it, op, ot, rate)
289+
@assert length(T) == length(rate)
290+
@assert all([p ∈ P for p in ip])
291+
@assert all([t ∈ T for t in it])
292+
@assert all([p ∈ P for p in op])
293+
@assert all([t ∈ T for t in ot])
294+
@assert I == length(ip)
295+
@assert I == length(it)
296+
@assert O == length(op)
297+
@assert O == length(ot)
298+
StochasticPetriNet(name, P, T, I, O, ip, it, op, ot, rate)
299+
end
300+
````
301+
302+
The structural components of the SIR model are all in the SPN generated below. Note that there are two output arcs
303+
from the "infection" transition, to the "I" compartment. This is the same as having a single arc of multiplicity 2,
304+
we model arcs "individually" here only to make the code cleaner and more readable.
305+
306+
````@example anderson
307+
sir_spn = make_stochasticpetrinet(
308+
"SIR", [:S,:I,:R], [:inf,:rec],
309+
3, 3,
310+
[:S,:I,:I], [:inf,:inf,:rec],
311+
[:I,:I,:R], [:inf,:inf,:rec],
312+
Dict((:inf => β), (:rec => γ))
313+
)
314+
````
315+
316+
Now we can write a function which generates a `ReactionSystem` from our SPN, assuming the law of mass action.
317+
The argument `X0` is the initial marking.
318+
319+
Note that in this simple example, we do not check the logical "enabling rules" for each transition, we directly
320+
compute the current rate/intensity. Because the net assumes the law of mass action, the computed rate will
321+
equal zero when the transition is not enabled, but this is not true of more general SPNs. A complete implementation
322+
would compute enabling rules from input arcs, and require the user to specify the rate as a `Function` that computed
323+
the intensity of that transition if the enabling rule for that transition evaluated to `true`. We would also
324+
want to apply the "consumption" of input tokens and the "production" of output tokens seperately, rather
325+
than compute the difference of consumption and production as the overall difference, as done here.
326+
327+
````@example anderson
328+
function generate_reaction_system(spn::StochasticPetriNet, X0)
329+
330+
mass_action_rs = make_reactionsystem(getname(spn), X0)
331+
332+
# for each transition, we must make a stochastic clock in the reaction system
333+
for t in spn.T
334+
# get the vector of preconditions (number of times each place is an input for this transition)
335+
precond = zeros(Int, length(spn.P))
336+
# get a vector of input indices
337+
precond_ix = Int[]
338+
for i in eachindex(spn.it)
339+
if spn.it[i] != t
340+
continue
341+
else
342+
push!(precond_ix, findfirst(isequal(spn.ip[i]), spn.P))
343+
precond[precond_ix[end]] += 1
344+
end
345+
end
346+
# get the vector of postconditions (number of times each places is an output for this transition)
347+
postcond = zeros(Int, length(spn.P))
348+
for i in eachindex(spn.ot)
349+
if spn.ot[i] != t
350+
continue
351+
else
352+
postcond[findfirst(isequal(spn.op[i]), spn.P)] += 1
353+
end
354+
end
355+
# total change to the marking as a result of transition t
356+
change = postcond - precond
357+
# add a stochastic clock to the reaction system for transition t
358+
add_clock!(
359+
mass_action_rs, String(t), (x) -> prod(x[precond_ix])*spn.rate[t], change
360+
)
361+
end
362+
363+
return mass_action_rs
364+
end
365+
````
366+
367+
Now we can generate the reaction system which implements the stochastic dynamics of the SIR model from
368+
the Petri net representing the structural constraints of the SIR model. In this way, we seperate specification
369+
of structure from specification of dynamics. We use the same initial condition as before.
370+
371+
````@example anderson
372+
x0 = [990, 10, 0]
373+
sir_rs = generate_reaction_system(sir_spn, x0)
374+
````
375+
376+
We now run another simulation.
377+
378+
````@example anderson
379+
simulate(sir_rs, floatmax(Float64))
380+
````
381+
382+
We can make another plot. Although the parameters are the same, the stochastic trajectory should look a little different,
383+
due to the randomness in the two driving Poisson processes.
384+
385+
````@example anderson
386+
df_out = select(sir_rs.df_output, Not(:clock));
387+
plot(df_out[!,:time], Matrix(df_out[:,[:X1,:X2,:X3]]), label = ["S" "I" "R"])
388+
````
389+

0 commit comments

Comments
 (0)