Skip to content

Commit

Permalink
finish disease_strains.jl
Browse files Browse the repository at this point in the history
  • Loading branch information
slwu89 committed Mar 12, 2024
1 parent d5ce8c1 commit 76fa519
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 29 deletions.
64 changes: 40 additions & 24 deletions docs/literate/covid/disease_strains.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ using DisplayAs, Markdown

# This example presents models incorporating multiple strains of disease and vaccine type.
# Importantly, it shows why stratification by disease strain is different from other stratifications, e.g. geography or age, and requires using a different type system.
# If you are unfamiliar with stratification, we reccomend first reading the [stratification tutorial](@ref stratification_example).

# ## Define basic epidemiology model

# We start by defining our basic type system for infectious disease models.
# We start by defining our basic type system, $P_{infectious}$ for infectious disease models.

const infectious_ontology = LabelledPetriNet(
[:Pop],
Expand All @@ -24,9 +25,9 @@ const infectious_ontology = LabelledPetriNet(

to_graphviz(infectious_ontology)

# We define a simple SIRD model with reflexive transitions typed as `:strata` to indicate which states can be stratified.
# Here we add reflexive transitions to the susceptible, infected, and recovered populations but we leave out the dead
# population because they cannot do things such as get vaccinated or travel between regions.
# We define a simple SIRD model with reflexive transitions typed as "strata" to indicate which stratified
# states will interact with transitions amongst strata. All but the dead population will have reflexive
# transitions amongst strata because no individuals may leave any death state.

sird_uwd = @relation (S,I,R,D) where (S::Pop, I::Pop, R::Pop, D::Pop) begin
infect(S, I, I, I)
Expand All @@ -41,9 +42,10 @@ to_graphviz(dom(sird_model))

# ## Define a model of multiple vaccine types

# We also define a model of vaccination with multiple vaccine types.
# In this model, vaccination transitions are typed as `:strata`.
# Note that the `:infect` transitions must be included to enable cross-infection between different vax types.
# We also define a model of vaccination with multiple vaccine types, typed over $P_{infectious}$.
# Each stratum has reflexive "disease" transitions. Transitions which represent the administration
# of a vaccine to an individual are typed as "strata". Infection (typed as "infect") between individuals of different
# vaccination status is possible (i.e.; we do not assume a perfect vaccine).

function vax_model(n)
uwd = RelationDiagram(repeat([:Pop], n+1))
Expand Down Expand Up @@ -129,9 +131,10 @@ to_graphviz(dom(strain_model′(2)))

# ## Stratify the SIRD model for two strains

# Unfortunately, stratification of these models does not produce the desired result.
# There are quite a few extraneous states and transitions.
# The primary issue is the asymmetry in the role of the uninfected population.
# Unfortunately, stratification of these models does not produce the desired result, leading
# to many states and transitions. The problem is that it does not make sense for the
# uninfected population to be stratified over the full set of states of the SIRD model
# (i.e.; uninfected persons can never have any disease state other than "S").
# We can address this by changing the type system.

typed_product(sird_model, strain_model′(2)) |> dom |> to_graphviz
Expand All @@ -153,7 +156,8 @@ const strain_ontology = LabelledPetriNet(

to_graphviz(strain_ontology)

# We now reform the SIRD model using the new type system.
# We now reform the SIRD model using the new type system. Note that the `where` clause is used to define
# the types for boxes in the UWD.
sird_for_strains_uwd = @relation (S,I,R,D) where (S::Uninf, I::Inf, R::Inf, D::Inf) begin
infect(S, I, I, I)
disease(I, R)
Expand Down Expand Up @@ -194,23 +198,29 @@ end

to_graphviz(dom(strain_model(2)))

# When we now stratify we get the desired model.
# When we now stratify we get the desired model. Note however that there are extraneous "strata"
# transitions; this is because there were no non-trivial stratum transitions in either $\phi$ or $\phi^{'}$
# (note that only "disease" and "infect" transition types were needed to fully define the multiple strain dynamics).

sird_strain = typed_product(sird_for_strains_model, strain_model(2))

to_graphviz(dom(sird_strain))

# ## Post-composition: Typing the type system
# ## Post-composition: Retyping the type system

# In some instances, we may want to relate models typed to different type systems.
# For example, we usually type our `simple_trip` model of geographic regions to the `infectious_ontology` such that we can stratify a disease model by geographic regions,
# but the multi-strain disease model above is typed by the new `strain_ontology`.

# Crucially, we can accomplish this IF there is an appropriate morphism (map) between the type systems because post-composition by a morphism of type systems is functorial.
# Crucially, we can accomplish this IF there is an appropriate morphism (map) between the type systems
# ($P_{strain}$ and $P_{infectious}$) because post-composition by a morphism of type systems is functorial.
# In this case, there is a morphism from `strain_ontology` to `infectious_ontology`, so we can form the morphism

# ### Morphism from `strain_ontology` to `infectious_ontology`

# We use `oapply_typed` on a UWD representation of the `strain_ontology`, but note that we could also directly
# define the map $P_{strain}\to P_{infectious}$ with `ACSetTransformation`.

strain_ont_uwd = @relation (Uninf,Inf) where (Uninf::Pop, Inf::Pop) begin
infect(Uninf, Inf, Inf, Inf)
disease(Inf, Inf)
Expand All @@ -226,9 +236,8 @@ to_graphviz(strain_ont_act)
# To demonstrate stratification utilizing post-composition to re-type the models, we use the simple-trip geographic model.
# This model is comprises a travel model and a living model.

# **Travel model between $N$ regions**\n
# In this model, there are $N$ regions which people can travel between. People within the same region are able
# to infect other people in the same region.
# to infect other people in the same region. It is typed by `infectious_ontology` ($P_{infectious}$).

function travel_model(n)
uwd = RelationDiagram(repeat([:Pop], n))
Expand Down Expand Up @@ -256,8 +265,8 @@ to_graphviz(dom(travel_model(2)))
# This model could itself be stratified with the SIRD model, but we want to model
# persons travelling between locations while maintaining the status of where they live.

# **Living model of $N$ regions**\n
# In this model, people have the property of "Living" somewhere.
# In this model, people have the property of "Living" somewhere.
# It is also typed by `infectious_ontology` ($P_{infectious}$).

function living_model(n)
typed_living = pairwise_id_typed_petri(infectious_ontology, :Pop, :infect, [Symbol("Living$(i)") for i in 1:n])
Expand All @@ -266,29 +275,34 @@ end

to_graphviz(dom(living_model(2)))

# **Simple trip model of $N$ regions**\n
# We can stratify the living model with the travel model to get a model of someone taking a simple trip.

simple_trip_model = typed_product(travel_model(2), living_model(2))

to_graphviz(dom(simple_trip_model))

# ### Stratify SIRD-multi-strain and simple-trip models
# ### Stratify multi-strain SIRD and simple-trip models

# Now, to stratify our multi-strain SIRD model with the simple-trip, we first retype the multi-strain model
# to the `infectious_ontology` by composing with the morphism we defined.
# to the `infectious_ontology` by composing with the morphism we defined. Mathematically, because
# the multi-strain SIRD model is a typed Petri net, it is a morphism $\phi : P \to P_{strain}$.
# The object `strain_ont_act` we made earlier is a morphism between $P_{strain} \to P_{infectious}$,
# so when we post-compose, we get a new morphism between $P \to P_{infectious}$.

sird_strain_retyped = compose(sird_strain,strain_ont_act)

# We can now stratify.
# We can now take the typed product to get the multi-strain SIRD model stratified over the simple trip model
# of movement between different regions.

sird_strain_trip = typed_product(sird_strain_retyped,simple_trip_model)

to_graphviz(dom(sird_strain_trip))

# ## Define a multi-strain SIRD model with vaccination by multiple vaccine types

# We can similarly stratify the multi-strain SIRD model with the multi-vax model.
# Because the multi-vaccine model was typed according to $P_{infectious}$, our retyped
# multi-strain SIRD can be stratified according to the multiple vaccine model in a
# similar way.

sird_strain_vax = typed_product(sird_strain_retyped,vax_model(2))

Expand All @@ -299,9 +313,11 @@ to_graphviz(dom(sird_strain_vax))
# If we would like to re-stratify our SIRD-strain-vax model with the simple trip model, we again face a difficulty.
# Both the "vaccination" transitions of the first model and the "travel" transitions of the second
# are currently typed to the `:strata` transition of the `infectious_ontology` type system.
# Naively stratifying would thus produce transitions in which persons traveled and were vaccinated simultaneously.

# To appropriately stratify, we need an additional "strata" transition to distinguish
# between the two types of transitions.
# We can again use post-compostion to overcome the difficulty.
# We can again use post-compostion with the morphism between type systems to reuse our existing models.

# ### Define an augmented version of the `infectious_ontology` type system with an additional "strata" transition

Expand Down
22 changes: 17 additions & 5 deletions docs/literate/covid/stratification.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ to_graphviz(infectious_ontology)
# The typed Petri net is modified with reflexive transitions typed as `:strata` to indicate which states can be stratified
# Here we add reflexive transitions to the susceptible, infected, and recovered populations but we leave out the dead
# population as no individuals entering that compartment may leave.
#
# Calling `to_graphviz` on the resulting object will display $\phi : P \to P_{infectious}$. We could apply `dom` (domain)
# or `codom` (codomain) on the object to extract $P$ or $P_{infectious}$, respectively, if we just wanted to plot
# a single Petri net.

sird_uwd = @relation (S,I,R,D) where (S::Pop, I::Pop, R::Pop, D::Pop) begin
infect(S, I, I, I)
Expand All @@ -55,7 +59,7 @@ end
sird_model = oapply_typed(infectious_ontology, sird_uwd, [:infection, :recovery, :death])
sird_model = add_reflexives(sird_model, [[:strata], [:strata], [:strata], []], infectious_ontology)

to_graphviz(dom(sird_model))
to_graphviz(sird_model)

# ## Define intervention models

Expand Down Expand Up @@ -91,6 +95,10 @@ typed_product(sird_model, mask_model) |> dom |> to_graphviz

# ### Vaccine model

# By changing ``P^{'}`` slightly, we can generate a stratification scheme to model vaccination. In this case,
# the transition between strata is irreversible (i.e.; the vaccine is not leaky), infection may occur between any
# pair of individuals, and change in disease state may occur in any strata.

vax_uwd = @relation (UV,V) where (UV::Pop, V::Pop) begin
strata(UV, V)
infect(V, V, V, V)
Expand All @@ -103,7 +111,7 @@ vax_model = add_reflexives(vax_model, [[:disease], [:disease]], infectious_ontol

to_graphviz(dom(vax_model))

# Stratify our SIRD model on this vaccine model to get a model of SIRD with a vaccination rate:
# Once again, the typed product, or pullback gives the SIRD model stratified by vaccination.

typed_product(sird_model, vax_model) |> dom |> to_graphviz

Expand Down Expand Up @@ -142,9 +150,10 @@ typed_product(sird_model, mask_vax_model) |> dom |> to_graphviz

# ### Travel model between $N$ regions

# For this model we can use a julia function to programmatically build up our undirected wiring diagram for defining this model.
# Here we want there to be $N$ regions in which people can travel between each region and people within the same region are able
# to infect other people in the same region.
# In many cases, it is not practical to construct by hand the undirected wiring diagram used for composition. Here we demonstrate
# how to use the imperative interface provided by Catlab to construct a UWD describing a model of a population spread out amongst $N$
# regions. Individuals can travel between regions, but disease transmission is only possible between individuals in the same region.
# The result is a typed Petri net describing this travel model.

function travel_model(n)
uwd = RelationDiagram(repeat([:Pop], n))
Expand Down Expand Up @@ -178,6 +187,9 @@ typed_product(sird_model, travel_model(2)) |> dom |> to_graphviz
# For this model we can use a julia function to programmatically build up our model where people have the property of living somewhere
# and we are modelling them travelling between locations while maintaining the status of where they live. Here we can actually just
# define the model of having a "Living" status and stratify it with the previously defined travel model to get a model of someone taking a simple trip.
# In the "living model" we allow for infections between individuals who live at different places; when we take the typed
# product with the simple trip model this will result in a model where infection is allowed between individuals at
# the same region, regardless of their home region.

function living_model(n)
typed_living = pairwise_id_typed_petri(infectious_ontology, :Pop, :infect, [Symbol("Living$(i)") for i in 1:n])
Expand Down

0 comments on commit 76fa519

Please sign in to comment.