Skip to content

Commit a747aad

Browse files
swoafmagee42
andcommitted
Convert demo to python (#9)
* convert demo to python * Update scratch/demo.qmd Co-authored-by: afmagee42 <[email protected]> --------- Co-authored-by: afmagee42 <[email protected]>
1 parent 6d542e1 commit a747aad

File tree

3 files changed

+52
-47
lines changed

3 files changed

+52
-47
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,3 +379,5 @@ docs/site/
379379
# such, it should not be committed for packages, but should be committed for
380380
# applications that require a static environment.
381381
#Manifest.toml
382+
*_ipynb
383+
*_files/

poetry.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scratch/demo.qmd

Lines changed: 46 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,97 +3,96 @@ title: Next-generation matrix demo
33
format: html
44
---
55

6+
```{python}
7+
import numpy as np
8+
```
9+
610
The next generation matrix $K$ has entries $K_{ij}$. Every infection in group $i$ in this generation will give rise to $K_{ij}$ infections in group $j$ in the next generation.
711

812
In this example, there are four groups. The core group transmits many infections to themselves and to travelers, who in turn transfer many infections to the core group (but not to themselves). Children and the general public don't transmit very much.
913

10-
```{r}
14+
```{python}
1115
# high and low transmission rates
12-
hi <- 3.0
13-
lo <- 0.5
14-
15-
K <- matrix(c(
16-
hi, lo, hi, lo, # core
17-
lo, lo, lo, lo, # kids
18-
hi, lo, lo, lo, # travelers
19-
lo, lo, lo, lo # general
20-
), nrow = 4, ncol = 4)
16+
hi = 3.0
17+
lo = 0.5
18+
19+
K = np.array(
20+
[
21+
[hi, lo, hi, lo], # core
22+
[lo, lo, lo, lo], # children
23+
[hi, lo, lo, lo], # travelers
24+
[lo, lo, lo, lo], # general
25+
]
26+
)
2127
```
2228

2329
Given a length-4 vector of infections $x$, representing the number of infections in each group in some generation, the next generation will have $Kx$ infections in each group.
2430

2531
By definition, $v$ and $\lambda$ are an eigenvector and eigenvalue of $K$ if $Kv = \lambda v$:
2632

27-
```{r}
33+
```{python}
2834
# do the eigenvalue analysis
29-
e <- eigen(K)
35+
e = np.linalg.eig(K)
3036
# which eigenvalue is the dominant one?
31-
i <- which.max(e$values)
32-
dominant_eigenvalue <- e$values[i]
37+
i = np.argmax(np.abs(e.eigenvalues))
38+
dominant_eigenvalue = e.eigenvalues[i]
3339
# get the corresponding eigenvector
34-
v <- e$vectors[, i]
40+
v = e.eigenvectors[:, i]
3541
3642
# note that this vector is L2-normalized
3743
# (I need all.equal because there is some machine precision limitation)
38-
stopifnot(all.equal(sqrt(sum(v ** 2)), 1.0))
44+
assert np.isclose(np.sqrt(sum(v**2)), 1.0)
3945
4046
# but we want an L1-normalized vector
41-
dominant_eigenvector <- v / sum(v)
47+
dominant_eigenvector = v / sum(v)
4248
43-
print("Dominant eigenvalue:")
44-
print(dominant_eigenvalue)
45-
print("Dominant eigenvector:")
46-
print(dominant_eigenvector)
49+
print("Dominant eigenvalue: ", dominant_eigenvalue)
50+
print("Dominant eigenvector:", dominant_eigenvector)
4751
```
4852

4953
Let's do an example, to show how these would play out. Start with a single infection, in the core group:
5054

51-
```{r}
52-
x0 <- c(1, 0, 0, 0)
55+
```{python}
56+
x0 = np.array([1.0, 0.0, 0.0, 0.0])
5357
```
5458

5559
Then iteratively generate next generations:
5660

57-
```{r}
58-
n_generations <- 10
61+
```{python}
62+
n_generations = 10
5963
60-
x <- x0
61-
results <- list(x0)
62-
for (i in 1:(n_generations - 1)) {
63-
x <- K %*% x
64-
results <- c(results, list(as.vector(x)))
65-
}
64+
x = x0
65+
66+
results = [x0]
67+
68+
for i in range(n_generations - 1):
69+
x = np.matmul(K, x)
70+
results.append(x)
6671
```
6772

6873
Toward the end of the simulation, $x$ will be large, but it has the same distribution as the L1-normalized dominant eigenvector:
6974

70-
```{r}
71-
print("Final numbers of infections:")
72-
last_x <- results[[length(results)]]
73-
print(last_x)
75+
```{python}
76+
print("Final numbers of infections: ", results[-1])
7477
75-
print("Same thing, L1-normalized:")
76-
print(last_x / sum(last_x))
78+
print("Same thing, L1-normalized:", results[-1] / sum(results[-1]))
7779
```
7880

7981
Note that this is the same as the L1-normalized dominant eigenvector.
8082

8183
We can also check how the number of infections changes from generation to generation:
8284

83-
```{r}
85+
```{python}
8486
# get the total number of infections per generation
85-
total_infections <- purrr::map_dbl(results, sum)
86-
print("Total infections per generation:")
87-
print(total_infections)
87+
total_infections = [sum(x) for x in results]
88+
print("Total infections per generation:", total_infections)
8889
8990
# and the ratio of infections between generations
90-
empirical_r <- purrr::map_dbl(
91-
2:n_generations,
92-
function(i) total_infections[i] / total_infections[i - 1]
93-
)
91+
empirical_r = [
92+
total_infections[i + 1] / total_infections[i] for i in range(n_generations - 1)
93+
]
9494
95-
print("Ratio of infections between generations")
96-
print(empirical_r)
95+
print("Ratio of infections between generations: ", empirical_r)
9796
```
9897

9998
Note that this stabilizes to the dominant eigenvalue.

0 commit comments

Comments
 (0)