Skip to content

Commit

Permalink
readme
Browse files Browse the repository at this point in the history
  • Loading branch information
stevencarlislewalker committed Dec 4, 2023
1 parent 44181f9 commit bbbd8b2
Show file tree
Hide file tree
Showing 2 changed files with 275 additions and 0 deletions.
164 changes: 164 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<!-- Auto-generated - do not edit by hand -->
<!-- Edit misc/build/README.Rmd instead -->
# macpan2

<!-- badges: start -->
Expand Down Expand Up @@ -173,6 +174,169 @@ TODO: - \[ \] Reuse the tools for the older concept of starter models. -

### Model Structure and Bookkeeping

Structured models are combinations of simpler modular model components.
For example one might combine an SIR model with an age-group contact
model to produce an age structured model. The modular model components
are called atomic models.

Models are composed of expression lists. Each expression in an
unstructured model can be converted into a structured expression to
create a structured model. For example, the following unstructured
expression defines the rate at which new infections emerge.

infection ~ beta * S * I / N

Each symbol in this expression has a certain type within a structured
model, and this type determines how it gets translated into a structured
expression. The simplest structured model is one that collects `S` and
`I` into a `state` vector with elements `S` and `I`. With this
interpretation of the `S` and `I` symbols, the structured infection
expression gets translated internally to the following.

infection ~ beta * state[S] * state[I] / N

Here `S` and `I` become symbols for extracting subsets of the `state`
vector. In this case the expression itself remains a scalar expression
but two of the scalars are obtained by extracting subsets of the `state`
vector. It won’t take much imagination to think of examples where
multiple paths to infection are required, and therefore the single
scalar-valued infection expression will be insufficient.

We will have a vector-valued expression, for example, in a model with an
expanded state vector that tracks the geographic location of `S` and `I`
individuals. For example, a two patch model with an `east` and `west`
patch would involve a four-dimensional state vector with the following
elements: `S.east`, `S.west`, `I.east`, and `I.west`. In this case we
now have two scalar-valued infection expressions.

infection[east] ~ beta * state[S.east] * state[I.east] / N
infection[west] ~ beta * state[S.west] * state[I.west] / N

With two patches it is fine to write out all scalar-valued infection
expressions, but with more patches and with different types of structure
(e.g. age groups, infection status, hospitalization, immunity status,
etc …) it will become crucial to have software that handles the
bookkeeping internally.

To see how easy this can be, note that this two-patch infection
expression can be powerfully and compactly expressed as our original
unstructured expression, `infection ~ beta * S * I / N`, where
`S = c(state[S.east], state[S.west])` and
`I = c(state[I.east], state[I.west])`.

Why is this powerful? Because it separates the math of the dynamic
mechanism, `infection ~ beta * S * I / N`, from the bookkeeping required
in structured models where the same mechanism is applied again and again
to different model strata. This is often how modellers think. For
example, I might have a location-structured SIR model that I need to
expand to be both age- and location-structured. In this case, infection
is still the same process, whereby a susceptible individual contacts an
infectious individual to create a flow from susceptible individuals to
infectious individuals. The same math applies to all strata of the
model. The boring but necessary part is to connect the math to the
bookkeeping associated with the model structure, and so software should
focus on making these bookkeeping changes as easy as possible and with
minimal changes required to the underlying mathematical expressions.

Let’s look at more examples of infection, and watch the bookkeeping get
more annoying. In an age-stratified model with two age groups, we now
get four scalar-valued infection expressions of the form
`infection ~ beta * S * I / N`.

infection[young.young] ~ beta[young.young] * state[S.young] * state[I.young] / N[young]
infection[young.old] ~ beta[young.old] * state[S.young] * state[I.old] / N[old]
infection[old.young] ~ beta[old.young] * state[S.old] * state[I.young] / N[young]
infection[old.old] ~ beta[old.old] * state[S.old] * state[I.old] / N[old]

Here the first expression is for a young individual infecting an old
individual, the second is for an old individual infecting a young
individual, etc … Things get worse if we have two age groups in two
patches.

infection[young.young.east] ~ beta[young.young.east] * state[S.young.east] * state[I.young.east] / N[young.east]
infection[young.old.east] ~ beta[young.old.east] * state[S.young.east] * state[I.old.east] / N[old.east]
infection[old.young.east] ~ beta[old.young.east] * state[S.old.east] * state[I.young.east] / N[young.east]
infection[old.old.east] ~ beta[old.old.east] * state[S.old.east] * state[I.old.east] / N[old.east]
infection[young.young.west] ~ beta[young.young.west] * state[S.young.west] * state[I.young.west] / N[young.west]
infection[young.old.west] ~ beta[young.old.west] * state[S.young.west] * state[I.old.west] / N[old.west]
infection[old.young.west] ~ beta[old.young.west] * state[S.old.west] * state[I.young.west] / N[young.west]
infection[old.old.west] ~ beta[old.old.west] * state[S.old.west] * state[I.old.west] / N[old.west]

This still isn’t so bad, as we just have the first four expressions for
`east` and the last four for `west`. But now let’s introduce two
infection status categories: `mild` and `severe`.

infection[young.young.east.mild] ~ beta[young.young.east.mild] * state[S.young.east.] * state[I.young.east.mild] / N[young.east]
infection[young.old.east.mild] ~ beta[young.old.east.mild] * state[S.young.east.] * state[I.old.east.mild] / N[old.east]
infection[old.young.east.mild] ~ beta[old.young.east.mild] * state[S.old.east.] * state[I.young.east.mild] / N[young.east]
infection[old.old.east.mild] ~ beta[old.old.east.mild] * state[S.old.east.] * state[I.old.east.mild] / N[old.east]
infection[young.young.east.severe] ~ beta[young.young.east.severe] * state[S.young.east.] * state[I.young.east.severe] / N[young.east]
infection[young.old.east.severe] ~ beta[young.old.east.severe] * state[S.young.east.] * state[I.old.east.severe] / N[old.east]
infection[old.young.east.severe] ~ beta[old.young.east.severe] * state[S.old.east.] * state[I.young.east.severe] / N[young.east]
infection[old.old.east.severe] ~ beta[old.old.east.severe] * state[S.old.east.] * state[I.old.east.severe] / N[old.east]
infection[young.young.west.mild] ~ beta[young.young.west.mild] * state[S.young.west.] * state[I.young.west.mild] / N[young.west]
infection[young.old.west.mild] ~ beta[young.old.west.mild] * state[S.young.west.] * state[I.old.west.mild] / N[old.west]
infection[old.young.west.mild] ~ beta[old.young.west.mild] * state[S.old.west.] * state[I.young.west.mild] / N[young.west]
infection[old.old.west.mild] ~ beta[old.old.west.mild] * state[S.old.west.] * state[I.old.west.mild] / N[old.west]
infection[young.young.west.severe] ~ beta[young.young.west.severe] * state[S.young.west.] * state[I.young.west.severe] / N[young.west]
infection[young.old.west.severe] ~ beta[young.old.west.severe] * state[S.young.west.] * state[I.old.west.severe] / N[old.west]
infection[old.young.west.severe] ~ beta[old.young.west.severe] * state[S.old.west.] * state[I.young.west.severe] / N[young.west]
infection[old.old.west.severe] ~ beta[old.old.west.severe] * state[S.old.west.] * state[I.old.west.severe] / N[old.west]

Above we used dot-concatenation to represent the two model strata
(epidemiological status and geographic location) in the state variable
names: `S.east`, `S.west`, `I.east`, and `I.west`. But as the state
vector gets more structured it becomes more convenient to describe its
variables using an index table, the rows of which describe each state
variable.

state = mp_cartesian(
mp_index(Epi = c("S", "I")),
mp_index(Loc = c("east", "west"))
)
state

## Epi Loc
## S east
## I east
## S west
## I west

With this representation we can get subsets of the state vector that
represent each epidemiological status.

mp_subset(state, Epi = "S")

## Epi Loc
## S east
## S west

mp_subset(state, Epi = "I")

## Epi Loc
## I east
## I west

- The symbols `S` and `I` are subsets of a structured vector, in this
case called `state`, and so are replaced in the following way.
- `S` -&gt; `state[S_infection]`
- `I` -&gt; `state[I_infection]`
- The functions `*` and `/` are binary operators in this case. In the
structured model they are vectorized, which means that `*` and `/`
is applied element-wise. For example, `S * I` means that the
resulting vector has a first element given by the product of the
first element of `S` and the first element of `I`, etc …

<!-- -->

flows_per_time[infection] ~ beta[infection_beta] * state[infection_S] * state[infection_I] / N[infection_N]

#### Structured Vectors

These are column vectors, the rows of which

### Structure Encourages Reparameterization

### Alternative Engines

### Combining Expression Lists
Expand Down
111 changes: 111 additions & 0 deletions misc/build/README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,117 @@ TODO:

### Model Structure and Bookkeeping

Structured models are combinations of simpler modular model components. For example one might combine an SIR model with an age-group contact model to produce an age structured model. The modular model components are called atomic models.

Models are composed of expression lists. Each expression in an unstructured model can be converted into a structured expression to create a structured model. For example, the following unstructured expression defines the rate at which new infections emerge.

```{r, eval = FALSE}
infection ~ beta * S * I / N
```

Each symbol in this expression has a certain type within a structured model, and this type determines how it gets translated into a structured expression. The simplest structured model is one that collects `S` and `I` into a `state` vector with elements `S` and `I`. With this interpretation of the `S` and `I` symbols, the structured infection expression gets translated internally to the following.

```{r, eval = FALSE}
infection ~ beta * state[S] * state[I] / N
```


Here `S` and `I` become symbols for extracting subsets of the `state` vector. In this case the expression itself remains a scalar expression but two of the scalars are obtained by extracting subsets of the `state` vector. It won't take much imagination to think of examples where multiple paths to infection are required, and therefore the single scalar-valued infection expression will be insufficient.

We will have a vector-valued expression, for example, in a model with an expanded state vector that tracks the geographic location of `S` and `I` individuals. For example, a two patch model with an `east` and `west` patch would involve a four-dimensional state vector with the following elements: `S.east`, `S.west`, `I.east`, and `I.west`. In this case we now have two scalar-valued infection expressions.

```{r, eval = FALSE}
infection[east] ~ beta * state[S.east] * state[I.east] / N
infection[west] ~ beta * state[S.west] * state[I.west] / N
```

With two patches it is fine to write out all scalar-valued infection expressions, but with more patches and with different types of structure (e.g. age groups, infection status, hospitalization, immunity status, etc ...) it will become crucial to have software that handles the bookkeeping internally.

To see how easy this can be, note that this two-patch infection expression can be powerfully and compactly expressed as our original unstructured expression, `infection ~ beta * S * I / N`, where `S = c(state[S.east], state[S.west])` and `I = c(state[I.east], state[I.west])`.

Why is this powerful? Because it separates the math of the dynamic mechanism, `infection ~ beta * S * I / N`, from the bookkeeping required in structured models where the same mechanism is applied again and again to different model strata. This is often how modellers think. For example, I might have a location-structured SIR model that I need to expand to be both age- and location-structured. In this case, infection is still the same process, whereby a susceptible individual contacts an infectious individual to create a flow from susceptible individuals to infectious individuals. The same math applies to all strata of the model. The boring but necessary part is to connect the math to the bookkeeping associated with the model structure, and so software should focus on making these bookkeeping changes as easy as possible and with minimal changes required to the underlying mathematical expressions.

Let's look at more examples of infection, and watch the bookkeeping get more annoying. In an age-stratified model with two age groups, we now get four scalar-valued infection expressions of the form `infection ~ beta * S * I / N`.
```{r, eval = FALSE}
infection[young.young] ~ beta[young.young] * state[S.young] * state[I.young] / N[young]
infection[young.old] ~ beta[young.old] * state[S.young] * state[I.old] / N[old]
infection[old.young] ~ beta[old.young] * state[S.old] * state[I.young] / N[young]
infection[old.old] ~ beta[old.old] * state[S.old] * state[I.old] / N[old]
```

Here the first expression is for a young individual infecting an old individual, the second is for an old individual infecting a young individual, etc ... Things get worse if we have two age groups in two patches.

```{r, eval = FALSE}
infection[young.young.east] ~ beta[young.young.east] * state[S.young.east] * state[I.young.east] / N[young.east]
infection[young.old.east] ~ beta[young.old.east] * state[S.young.east] * state[I.old.east] / N[old.east]
infection[old.young.east] ~ beta[old.young.east] * state[S.old.east] * state[I.young.east] / N[young.east]
infection[old.old.east] ~ beta[old.old.east] * state[S.old.east] * state[I.old.east] / N[old.east]
infection[young.young.west] ~ beta[young.young.west] * state[S.young.west] * state[I.young.west] / N[young.west]
infection[young.old.west] ~ beta[young.old.west] * state[S.young.west] * state[I.old.west] / N[old.west]
infection[old.young.west] ~ beta[old.young.west] * state[S.old.west] * state[I.young.west] / N[young.west]
infection[old.old.west] ~ beta[old.old.west] * state[S.old.west] * state[I.old.west] / N[old.west]
```

This still isn't so bad, as we just have the first four expressions for `east` and the last four for `west`. But now let's introduce two infection status categories: `mild` and `severe`.

```{r, eval = FALSE}
infection[young.young.east.mild] ~ beta[young.young.east.mild] * state[S.young.east.] * state[I.young.east.mild] / N[young.east]
infection[young.old.east.mild] ~ beta[young.old.east.mild] * state[S.young.east.] * state[I.old.east.mild] / N[old.east]
infection[old.young.east.mild] ~ beta[old.young.east.mild] * state[S.old.east.] * state[I.young.east.mild] / N[young.east]
infection[old.old.east.mild] ~ beta[old.old.east.mild] * state[S.old.east.] * state[I.old.east.mild] / N[old.east]
infection[young.young.east.severe] ~ beta[young.young.east.severe] * state[S.young.east.] * state[I.young.east.severe] / N[young.east]
infection[young.old.east.severe] ~ beta[young.old.east.severe] * state[S.young.east.] * state[I.old.east.severe] / N[old.east]
infection[old.young.east.severe] ~ beta[old.young.east.severe] * state[S.old.east.] * state[I.young.east.severe] / N[young.east]
infection[old.old.east.severe] ~ beta[old.old.east.severe] * state[S.old.east.] * state[I.old.east.severe] / N[old.east]
infection[young.young.west.mild] ~ beta[young.young.west.mild] * state[S.young.west.] * state[I.young.west.mild] / N[young.west]
infection[young.old.west.mild] ~ beta[young.old.west.mild] * state[S.young.west.] * state[I.old.west.mild] / N[old.west]
infection[old.young.west.mild] ~ beta[old.young.west.mild] * state[S.old.west.] * state[I.young.west.mild] / N[young.west]
infection[old.old.west.mild] ~ beta[old.old.west.mild] * state[S.old.west.] * state[I.old.west.mild] / N[old.west]
infection[young.young.west.severe] ~ beta[young.young.west.severe] * state[S.young.west.] * state[I.young.west.severe] / N[young.west]
infection[young.old.west.severe] ~ beta[young.old.west.severe] * state[S.young.west.] * state[I.old.west.severe] / N[old.west]
infection[old.young.west.severe] ~ beta[old.young.west.severe] * state[S.old.west.] * state[I.young.west.severe] / N[young.west]
infection[old.old.west.severe] ~ beta[old.old.west.severe] * state[S.old.west.] * state[I.old.west.severe] / N[old.west]
```



Above we used dot-concatenation to represent the two model strata (epidemiological status and geographic location) in the state variable names: `S.east`, `S.west`, `I.east`, and `I.west`. But as the state vector gets more structured it becomes more convenient to describe its variables using an index table, the rows of which describe each state variable.

```{r}
state = mp_cartesian(
mp_index(Epi = c("S", "I")),
mp_index(Loc = c("east", "west"))
)
state
```

With this representation we can get subsets of the state vector that represent each epidemiological status.

```{r}
mp_subset(state, Epi = "S")
mp_subset(state, Epi = "I")
```

* The symbols `S` and `I` are subsets of a structured vector, in this case called `state`, and so are replaced in the following way.
* `S` -> `state[S_infection]`
* `I` -> `state[I_infection]`
* The functions `*` and `/` are binary operators in this case. In the structured model they are vectorized, which means that `*` and `/` is applied element-wise. For example, `S * I` means that the resulting vector has a first element given by the product of the first element of `S` and the first element of `I`, etc ...


```{r, eval = FALSE}
flows_per_time[infection] ~ beta[infection_beta] * state[infection_S] * state[infection_I] / N[infection_N]
```

#### Structured Vectors

These are column vectors, the rows of which


### Structure Encourages Reparameterization




### Alternative Engines

### Combining Expression Lists
Expand Down

0 comments on commit bbbd8b2

Please sign in to comment.