### A Pluto.jl notebook ### # v0.12.3 using Markdown using InteractiveUtils # This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error). macro bind(def, element) quote local el = $(esc(element)) global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing el end end # ╔═╡ 2b37ca3a-0970-11eb-3c3d-4f788b411d1a begin using Pkg Pkg.activate(mktempdir()) end # ╔═╡ 2dcb18d0-0970-11eb-048a-c1734c6db842 begin Pkg.add(["PlutoUI", "Plots"]) using Plots gr() using PlutoUI end # ╔═╡ 19fe1ee8-0970-11eb-2a0d-7d25e7d773c6 md"_homework 5, version 0_" # ╔═╡ 49567f8e-09a2-11eb-34c1-bb5c0b642fe8 # WARNING FOR OLD PLUTO VERSIONS, DONT DELETE ME html""" """ # ╔═╡ 181e156c-0970-11eb-0b77-49b143cc0fc0 md""" # **Homework 5**: _Epidemic modeling II_ `18.S191`, fall 2020 This notebook contains _built-in, live answer checks_! In some exercises you will see a coloured box, which runs a test case on your code, and provides feedback based on the result. Simply edit the code, run it, and the check runs again. _For MIT students:_ there will also be some additional (secret) test cases that will be run as part of the grading process, and we will look at your notebook and write comments. Feel free to ask questions! """ # ╔═╡ 1f299cc6-0970-11eb-195b-3f951f92ceeb # edit the code below to set your name and kerberos ID (i.e. email without @mit.edu) student = (name = "Jazzy Doe", kerberos_id = "jazz") # you might need to wait until all other cells in this notebook have completed running. # scroll around the page to see what's up # ╔═╡ 1bba5552-0970-11eb-1b9a-87eeee0ecc36 md""" Submission by: **_$(student.name)_** ($(student.kerberos_id)@mit.edu) """ # ╔═╡ 2848996c-0970-11eb-19eb-c719d797c322 md"_Let's create a package environment:_" # ╔═╡ 69d12414-0952-11eb-213d-2f9e13e4b418 md""" In this problem set, we will look at a simple **spatial** agent-based epidemic model: agents can interact only with other agents that are *nearby*. (In the previous homework any agent could interact with any other, which is not realistic.) A simple approach is to use **discrete space**: each agent lives in one cell of a square grid. For simplicity we will allow no more than one agent in each cell, but this requires some care to design the rules of the model to respect this. We will adapt some functionality from the previous homework. You should copy and paste your code from that homework into this notebook. """ # ╔═╡ 3e54848a-0954-11eb-3948-f9d7f07f5e23 md""" ## **Exercise 1:** _Wandering at random in 2D_ In this exercise we will implement a **random walk** on a 2D lattice (grid). At each time step, a walker jumps to a neighbouring position at random (i.e. chosen with uniform probability from the available adjacent positions). """ # ╔═╡ 3e623454-0954-11eb-03f9-79c873d069a0 md""" #### Exercise 1.1 We define a struct type `Coordinate` that contains integers `x` and `y`. """ # ╔═╡ 0ebd35c8-0972-11eb-2e67-698fd2d311d2 # ╔═╡ 027a5f48-0a44-11eb-1fbf-a94d02d0b8e3 md""" 👉 Construct a `Coordinate` located at the origin. """ # ╔═╡ b2f90634-0a68-11eb-1618-0b42f956b5a7 origin = missing # ╔═╡ 3e858990-0954-11eb-3d10-d10175d8ca1c md""" 👉 Write a function `make_tuple` that takes an object of type `Coordinate` and returns the corresponding tuple `(x, y)`. Boring, but useful later! """ # ╔═╡ 189bafac-0972-11eb-1893-094691b2073c # function make_tuple(c) # missing # end # ╔═╡ 73ed1384-0a29-11eb-06bd-d3c441b8a5fc md""" #### Exercise 1.2 In Julia, operations like `+` and `*` are just functions, and they are treated like any other function in the language. The only special property you can use the _infix notation_: you can write ```julia 1 + 2 ``` instead of ```julia +(1, 2) ``` _(There are [lots of special 'infixable' function names](https://github.com/JuliaLang/julia/blob/master/src/julia-parser.scm#L23-L24) that you can use for your own functions!)_ When you call it with the prefix notation, it becomes clear that it really is 'just another function', with lots of predefined methods. """ # ╔═╡ 96707ef0-0a29-11eb-1a3e-6bcdfb7897eb +(1, 2) # ╔═╡ b0337d24-0a29-11eb-1fab-876a87c0973f + # ╔═╡ 9c9f53b2-09ea-11eb-0cda-639764250cee md""" > #### Extending + in the wild > Because it is a function, we can add our own methods to it! This feature is super useful in general languages like Julia and Python, because it lets you use familiar syntax (`a + b*c`) on objects that are not necessarily numbers! > > One example we've see before is the `RGB` type in Homework 1. You are able to do: > ```julia > 0.5 * RGB(0.1, 0.7, 0.6) > ``` > to multiply each color channel by $0.5$. This is possible because `Images.jl` [wrote a method](https://github.com/JuliaGraphics/ColorVectorSpace.jl/blob/master/src/ColorVectorSpace.jl#L131): > ```julia > *(::Real, ::AbstractRGB)::AbstractRGB > ``` 👉 Implement addition on two `Coordinate` structs by adding a method to `Base.:+` """ # ╔═╡ e24d5796-0a68-11eb-23bb-d55d206f3c40 # function Base.:+(a::TYPE, b::TYPE) # return missing # end # ╔═╡ ec8e4daa-0a2c-11eb-20e1-c5957e1feba3 # Coordinate(3,4) + Coordinate(10,10) # uncomment to check + works # ╔═╡ e144e9d0-0a2d-11eb-016e-0b79eba4b2bb md""" _Pluto has some trouble here, you need to manually re-run the cell above!_ """ # ╔═╡ 71c358d8-0a2f-11eb-29e1-57ff1915e84a md""" #### Exercise 1.3 In our model, agents will be able to walk in 4 directions: up, down, left and right. We can define these directions as `Coordinate`s. """ # ╔═╡ 5278e232-0972-11eb-19ff-a1a195127297 # uncomment this: # possible_moves = [ # Coordinate( 1, 0), # Coordinate( 0, 1), # Coordinate(-1, 0), # Coordinate( 0,-1), # ] # ╔═╡ 71c9788c-0aeb-11eb-28d2-8dcc3f6abacd md""" 👉 `rand(possible_moves)` gives a random possible move. Add this to the coordinate `Coordinate(4,5)` and see that it moves to a valid neighbor. """ # ╔═╡ 69151ce6-0aeb-11eb-3a53-290ba46add96 # ╔═╡ 3eb46664-0954-11eb-31d8-d9c0b74cf62b md""" We are able to make a `Coordinate` perform one random step, by adding a move to it. Great! 👉 Write a function `trajectory` that calculates a trajectory of a `Wanderer` `w` when performing `n` steps., i.e. the sequence of positions that the walker finds itself in. Possible steps: - Use `rand(possible_moves, n)` to generate a vector of `n` random moves. Each possible move will be equally likely. - To compute the trajectory you can use either of the following two approaches: 1. 🆒 Use the function `accumulate` (see the live docs for `accumulate`). Use `+` as the function passed to `accumulate` and the `w` as the starting value (`init` keyword argument). 1. Use a `for` loop calling `+`. """ # ╔═╡ edf86a0e-0a68-11eb-2ad3-dbf020037019 # function trajectory(w::Coordinate, n::Int) # return missing # end # ╔═╡ 44107808-096c-11eb-013f-7b79a90aaac8 # test_trajectory = trajectory(Coordinate(4,4), 30) # uncomment to test # ╔═╡ 478309f4-0a31-11eb-08ea-ade1755f53e0 function plot_trajectory!(p::Plots.Plot, trajectory::Vector; kwargs...) plot!(p, make_tuple.(trajectory); label=nothing, linewidth=2, linealpha=LinRange(1.0, 0.2, length(trajectory)), kwargs...) end # ╔═╡ 87ea0868-0a35-11eb-0ea8-63e27d8eda6e try p = plot(ratio=1, size=(650,200)) plot_trajectory!(p, test_trajectory; color="black", showaxis=false, axis=nothing, linewidth=4) p catch end # ╔═╡ 51788e8e-0a31-11eb-027e-fd9b0dc716b5 # let # long_trajectory = trajectory(Coordinate(4,4), 1000) # p = plot(ratio=1) # plot_trajectory!(p, long_trajectory) # p # end # ^ uncomment to visualize a trajectory # ╔═╡ 3ebd436c-0954-11eb-170d-1d468e2c7a37 md""" #### Exercise 1.4 👉 Plot 10 trajectories of length 1000 on a single figure, all starting at the origin. Use the function `plot_trajectory!` as demonstrated above. Remember from last week that you can compose plots like this: ```julia let # Create a new plot with aspect ratio 1:1 p = plot(ratio=1) plot_trajectory!(p, test_trajectory) # plot one trajectory plot_trajectory!(p, another_trajectory) # plot the second one ... p end ``` """ # ╔═╡ dcefc6fe-0a3f-11eb-2a96-ddf9c0891873 # ╔═╡ b4d5da4a-09a0-11eb-1949-a5807c11c76c md""" #### Exercise 1.5 Agents live in a box of side length $2L$, centered at the origin. We need to decide (i.e. model) what happens when they reach the walls of the box (boundaries), in other words what kind of **boundary conditions** to use. One relatively simple boundary condition is a **collision boundary**: > Each wall of the box is a wall, modelled using "collision": if the walker tries to jump beyond the wall, it ends up at the position inside the box that is closest to the goal. 👉 Write a function `collide_boundary` which takes a `Coordinate` `c` and a size $L$, and returns a new coordinate that lies inside the box (i.e. ``[-L,L]\times [-L,L]``), but is closest to `c`. This is similar to `extend_mat` from Homework 1. """ # ╔═╡ 0237ebac-0a69-11eb-2272-35ea4e845d84 # function collide_boundary(c::Coordinate, L::Number) # return missing # end # ╔═╡ ad832360-0a40-11eb-2857-e7f0350f3b12 # collide_boundary(Coordinate(12,4), 10) # uncomment to test # ╔═╡ b4ed2362-09a0-11eb-0be9-99c91623b28f md""" #### Exercise 1.6 👉 Implement a 3-argument method of `trajectory` where the third argument is a size. The trajectory returned should be within the boundary (use `collide_boundary` from above). You can still use `accumulate` with an anonymous function that makes a move and then reflects the resulting coordinate, or use a for loop. """ # ╔═╡ 0665aa3e-0a69-11eb-2b5d-cd718e3c7432 # function trajectory(c::Coordinate, n::Int, L::Number) # return missing # end # ╔═╡ 3ed06c80-0954-11eb-3aee-69e4ccdc4f9d md""" ## **Exercise 2:** _Wanderering Agents_ In this exercise we will create Agents which have a location as well as some infection state information. Let's define a type `Agent`. `Agent` contains a `position` (of type `Coordinate`), as well as a `status` of type `InfectionStatus` (as in Homework 4).) (For simplicity we will not use a `num_infected` field, but feel free to do so!) """ # ╔═╡ 35537320-0a47-11eb-12b3-931310f18dec @enum InfectionStatus S I R # ╔═╡ cf2f3b98-09a0-11eb-032a-49cc8c15e89c # define agent struct here: # ╔═╡ 814e888a-0954-11eb-02e5-0964c7410d30 md""" #### Exercise 2.1 👉 Write a function `initialize` that takes parameters $N$ and $L$, where $N$ is the number of agents and $2L$ is the side length of the square box where the agents live. It returns a `Vector` of `N` randomly generated `Agent`s. Their coordinates are randomly sampled in the ``[-L,L] \times [-L,L]`` box, and the agents are all susceptible, except one, chosen at random, which is infectious. """ # ╔═╡ 0cfae7ba-0a69-11eb-3690-d973d70e47f4 # function initialize(N::Number, L::Number) # return missing # end # ╔═╡ 1d0f8eb4-0a46-11eb-38e7-63ecbadbfa20 # initialize(3, 10) # ╔═╡ e0b0880c-0a47-11eb-0db2-f760bbbf9c11 # Color based on infection status color(s::InfectionStatus) = if s == S "blue" elseif s == I "red" else "green" end # ╔═╡ b5a88504-0a47-11eb-0eda-f125d419e909 # position(a::Agent) = a.position # uncomment this line # ╔═╡ 87a4cdaa-0a5a-11eb-2a5e-cfaf30e942ca # color(a::Agent) = color(a.status) # uncomment this line # ╔═╡ 49fa8092-0a43-11eb-0ba9-65785ac6a42f md""" #### Exercise 2.2 👉 Write a function `visualize` that takes in a collection of agents as argument, and the box size `L`. It should plot a point for each agent at its location, coloured according to its status. You can use the keyword argument `c=color.(agents)` inside your call to the plotting function make the point colors correspond to the infection statuses. Don't forget to use `ratio=1`. """ # ╔═╡ 1ccc961e-0a69-11eb-392b-915be07ef38d # function visualize(agents::Vector, L) # return missing # end # ╔═╡ 1f96c80a-0a46-11eb-0690-f51c60e57c3f let N = 20 L = 10 # visualize(initialize(N, L), L) # uncomment this line! end # ╔═╡ f953e06e-099f-11eb-3549-73f59fed8132 md""" ### Exercise 3: Spatial epidemic model -- Dynamics Last week we wrote a function `interact!` that takes two agents, `agent` and `source`, and an infection of type `InfectionRecovery`, which models the interaction between two agent, and possibly modifies `agent` with a new status. This week, we define a new infection type, `CollisionInfectionRecovery`, and a new method that is the same as last week, except it **only infects `agent` if `agents.position==source.position`**. """ # ╔═╡ e6dd8258-0a4b-11eb-24cb-fd5b3554381b abstract type AbstractInfection end # ╔═╡ de88b530-0a4b-11eb-05f7-85171594a8e8 struct CollisionInfectionRecovery <: AbstractInfection p_infection::Float64 p_recovery::Float64 end # ╔═╡ 80f39140-0aef-11eb-21f7-b788c5eab5c9 md""" Write a function `interact!` that takes two `Agent`s and a `CollisionInfectionRecovery`, and: - If the agents are at the same spot, causes a susceptible agent to communicate the desease from an infectious one with the correct probability. - if the first agent is infectious, it recovers with some probability """ # ╔═╡ d1bcd5c4-0a4b-11eb-1218-7531e367a7ff #function interact!(agent::Agent, source::Agent, infection::CollisionInfectionRecovery) #missing #end # ╔═╡ 34778744-0a5f-11eb-22b6-abe8b8fc34fd md""" #### Exercise 3.1 Your turn! 👉 Write a function `step!` that takes a vector of `Agent`s, a box size `L` and an `infection`. This that does one step of the dynamics on a vector of agents. - Choose an Agent `source` at random. - Move the `source` one step, and use `collide_boundary` to ensure that our agent stays within the box. - For all _other_ agents, call `interact!(other_agent, source, infection)`. - return the array `agents` again. """ # ╔═╡ 24fe0f1a-0a69-11eb-29fe-5fb6cbf281b8 # function step!(agents::Vector, L::Number, infection::AbstractInfection) # return missing # end # ╔═╡ 1fc3271e-0a45-11eb-0e8d-0fd355f5846b md""" #### Exercise 3.2 If we call `step!` `N` times, then every agent will have made one step, on average. Let's call this one _sweep_ of the simulation. 👉 Create a before-and-after plot of ``k_{sweeps}=1000`` sweeps. - Initialize a new vector of agents (`N=50`, `L=40`, `infection` is given as `pandemic` below). - Plot the state using `visualize`, and save the plot as a variable `plot_before`. - Run `k_sweeps` sweeps. - Plot the state again, and store as `plot_after`. - Combine the two plots into a single figure using ```julia plot(plot_before, plot_after) ``` """ # ╔═╡ 18552c36-0a4d-11eb-19a0-d7d26897af36 pandemic = CollisionInfectionRecovery(0.5, 0.00001) # ╔═╡ 4e7fd58a-0a62-11eb-1596-c717e0845bd5 @bind k_sweeps Slider(1:10000, default=1000) # ╔═╡ 778c2490-0a62-11eb-2a6c-e7fab01c6822 # let # N = 50 # L = 40 # plot_before = plot(1:3) # replace with your code # plot_after = plot(1:3) # plot(plot_before, plot_after) # end # ╔═╡ e964c7f0-0a61-11eb-1782-0b728fab1db0 md""" #### Exercise 3.3 Every time that you move the slider, a completely new simulation is created an run. This makes it hard to view the progress of a single simulation over time. So in this exercise, we we look at a single simulation, and plot the S, I and R curves. 👉 Plot the SIR curves of a single simulation, with the same parameters as in the previous exercise. Use `k_sweep_max = 10000` as the total number of sweeps. """ # ╔═╡ 4d83dbd0-0a63-11eb-0bdc-757f0e721221 k_sweep_max = 10000 # ╔═╡ ef27de84-0a63-11eb-177f-2197439374c5 let N = 50 L = 30 # agents = initialize(N, L) # compute k_sweep_max number of sweeps and plot the SIR end # ╔═╡ 201a3810-0a45-11eb-0ac9-a90419d0b723 md""" #### Exercise 3.4 (optional) Let's make our plot come alive! There are two options to make our visualization dynamic: 👉1️⃣ Precompute one simulation run and save its intermediate states using `deepcopy`. You can then write an interactive visualization that shows both the state at time $t$ (using `visualize`) and the history of $S$, $I$ and $R$ from time $0$ up to time $t$. $t$ is controlled by a slider. 👉2️⃣ Use `@gif` from Plots.jl to turn a sequence of plots into an animation. Be careful to skip about 50 sweeps between each animation frame, otherwise the GIF becomes too large. This an optional exercise, and our solution to 2️⃣ is given below. """ # ╔═╡ e5040c9e-0a65-11eb-0f45-270ab8161871 # let # N = 50 # L = 30 # missing # end # ╔═╡ 2031246c-0a45-11eb-18d3-573f336044bf md""" #### Exercise 3.5 👉 Using $L=20$ and $N=100$, experiment with the infection and recovery probabilities until you find an epidemic outbreak. (Take the recovery probability quite small.) Modify the two infections below to match your observations. """ # ╔═╡ 63dd9478-0a45-11eb-2340-6d3d00f9bb5f causes_outbreak = CollisionInfectionRecovery(0.5, 0.001) # ╔═╡ 269955e4-0a46-11eb-02cc-1946dc918bfa does_not_cause_outbreak = CollisionInfectionRecovery(0.5, 0.001) # ╔═╡ 4d4548fe-0a66-11eb-375a-9313dc6c423d # ╔═╡ 20477a78-0a45-11eb-39d7-93918212a8bc md""" #### Exercise 3.6 👉 With the parameters of Exercise 3.2, run 50 simulations. Plot $S$, $I$ and $R$ as a function of time for each of them (with transparency!). This should look qualitatively similar to what you saw in the previous homework. You probably need different `p_infection` and `p_recovery` values from last week. Why? """ # ╔═╡ 601f4f54-0a45-11eb-3d6c-6b9ec75c6d4a # ╔═╡ b1b1afda-0a66-11eb-2988-752405815f95 need_different_parameters_because = md""" i say so """ # ╔═╡ 05c80a0c-09a0-11eb-04dc-f97e306f1603 md""" ## **Exercise 4:** _Effect of socialization_ In this exercise we'll modify the simple mixing model. Instead of a constant mixing probability, i.e. a constant probability that any pair of people interact on a given day, we will have a variable probability associated with each agent, modelling the fact that some people are more or less social or contagious than others. """ # ╔═╡ b53d5608-0a41-11eb-2325-016636a22f71 md""" #### Exercise 4.1 We create a new agent type `SocialAgent` with fields `position`, `status`, `num_infected`, and `social_score`. The attribute `social_score` represents an agent's probability of interacting with any other agent in the population. """ # ╔═╡ 1b5e72c6-0a42-11eb-3884-a377c72270c7 # struct SocialAgent here... # ╔═╡ c704ea4c-0aec-11eb-2f2c-859c954aa520 md"""define the `position` and `color` methods for `SocialAgent` as we did for `Agent`. This will allow the `visualize` function to work. on both kinds of Agents""" # ╔═╡ e97e39aa-0a5d-11eb-3d5f-f90a0acfe5a2 # begin # position(a::SocialAgent) = ... # color(a::SocialAgent) = ... # end # ╔═╡ b554b654-0a41-11eb-0e0d-e57ff68ced33 md""" 👉 Create a function `initialize_social` that takes `N` and `L`, and creates N agents within a 2L x 2L box, with `social_score`s chosen from 10 equally-spaced between 0.1 and 0.5. (see LinRange) """ # ╔═╡ 40c1c1d6-0a69-11eb-3913-59e9b9ec4332 # function initialize_social(N, L) # return missing # end # ╔═╡ 18ac9926-0aed-11eb-034f-e9849b71c9ac md""" Now that we have 2 agent types 1. let's create an AbstractAgent type 2. Go back in the notebook and make the agent types a subtype of AbstractAgent. """ # ╔═╡ b56ba420-0a41-11eb-266c-719d39580fa9 md""" #### Exercise 4.2 Not all two agents who end up in the same grid point may actually interact in an infectious way -- they may just be passing by and do not create enough exposure for communicating the disease. 👉 Write a new `interact!` method on `SocialAgent` which adds together the social_scores for two agents and uses that as the probability that they interact in a risky way. Only if they interact in a risky way, the infection is transmitted with the usual probability. """ # ╔═╡ 465e918a-0a69-11eb-1b59-01150b4b0f36 # function interact!(agent::SocialAgent, source::SocialAgent, infection::CollisionInfectionRecovery) # # your code here # end # ╔═╡ a885bf78-0a5c-11eb-2383-9d74c8765847 md""" Make sure `step!`, `position`, `color`, work on the type `SocialAgent`. If `step!` takes an untyped first argument, it should work for both Agent and SocialAgent types without any changes. We actually only need to specialize `interact!` on SocialAgent. #### Exercise 4.3 👉 Plot the SIR curves of the resulting simulation. N = 50; L = 40; number of steps = 200 In each step call `step!` 50N times. """ # ╔═╡ 1f172700-0a42-11eb-353b-87c0039788bd let N = 50 L = 40 #global social_agents = initialize_social(N, L) Ss, Is, Rs = [], [], [] Tmax = 200 @gif for t in 1:Tmax # 1. Step! a lot # 2. Count S, I and R, push them to Ss Is Rs # 3. call visualize on the agents, # 4. place the SIR plot next to visualize. # plot(left, right, size=(600,300)) # final plot end end # ╔═╡ b59de26c-0a41-11eb-2c67-b5f3c7780c91 md""" #### Exercise 4.4 👉 Make a scatter plot showing each agent's `social_score` on one axis, and the `num_infected` from the simulation in the other axis. Run this simulation several times and comment on the results. """ # ╔═╡ faec52a8-0a60-11eb-082a-f5787b09d88c # ╔═╡ b5b4d834-0a41-11eb-1b18-1bd626d18934 md""" 👉 Run a simulation for 100 steps, and then apply a "lockdown" where every agent's social score gets multiplied by 0.25, and then run a second simulation which runs on that same population from there. What do you notice? How does changing this factor form 0.25 to other numbers affect things? """ # ╔═╡ a83c96e2-0a5a-11eb-0e58-15b5dda7d2d2 # ╔═╡ 05fc5634-09a0-11eb-038e-53d63c3edaf2 md""" ## **Exercise 5:** (Optional) _Effect of distancing_ We can use a variant of the above model to investigate the effect of the mis-named "social distancing" (we want people to be *socially* close, but *physically* distant). In this variant, we separate out the two effects "infection" and "movement": an infected agent chooses a neighbouring site, and if it finds a susceptible there then it infects it with probability $p_I$. For simplicity we can ignore recovery. Separately, an agent chooses a neighbouring site to move to, and moves there with probability $p_M$ if the site is vacant. (Otherwise it stays where it is.) When $p_M = 0$, the agents cannot move, and hence are completely quarantined in their original locations. 👉 How does the disease spread in this case? """ # ╔═╡ 24c2fb0c-0a42-11eb-1a1a-f1246f3420ff # ╔═╡ c7649966-0a41-11eb-3a3a-57363cea7b06 md""" 👉 Run the dynamics repeatedly, and plot the sites which become infected. """ # ╔═╡ 2635b574-0a42-11eb-1daa-971b2596ce44 # ╔═╡ c77b085e-0a41-11eb-2fcb-534238cd3c49 md""" 👉 How does this change as you increase the *density* $\rho = N / (L^2)$ of agents? Start with a small density. This is basically the [**site percolation**](https://en.wikipedia.org/wiki/Percolation_theory) model. When we increase $p_M$, we allow some local motion via random walks. """ # ╔═╡ 274fe006-0a42-11eb-1869-29193bb84957 # ╔═╡ c792374a-0a41-11eb-1e5b-89d9de2cf1f9 md""" 👉 Investigate how this leaky quarantine affects the infection dynamics with different densities. """ # ╔═╡ d147f7f0-0a66-11eb-2877-2bc6680e396d # ╔═╡ 0e6b60f6-0970-11eb-0485-636624a0f9d7 if student.name == "Jazzy Doe" md""" !!! danger "Before you submit" Remember to fill in your **name** and **Kerberos ID** at the top of this notebook. """ end # ╔═╡ 0a82a274-0970-11eb-20a2-1f590be0e576 md"## Function library Just some helper functions used in the notebook." # ╔═╡ 0aa666dc-0970-11eb-2568-99a6340c5ebd hint(text) = Markdown.MD(Markdown.Admonition("hint", "Hint", [text])) # ╔═╡ 8475baf0-0a63-11eb-1207-23f789d00802 hint(md""" After every sweep, count the values $S$, $I$ and $R$ and push! them to 3 arrays. """) # ╔═╡ f9b9e242-0a53-11eb-0c6a-4d9985ef1687 hint(md""" ```julia let N = 50 L = 40 x = initialize(N, L) # initialize to empty arrays Ss, Is, Rs = Int[], Int[], Int[] Tmax = 200 @gif for t in 1:Tmax for i in 1:50N step!(x, L, pandemic) end #... track S, I, R in Ss Is and Rs left = visualize(x, L) right = plot(xlim=(1,Tmax), ylim=(1,N), size=(600,300)) plot!(right, 1:t, Ss, color=color(S), label="S") plot!(right, 1:t, Is, color=color(I), label="I") plot!(right, 1:t, Rs, color=color(R), label="R") plot(left, right) end end ``` """) # ╔═╡ 0acaf3b2-0970-11eb-1d98-bf9a718deaee almost(text) = Markdown.MD(Markdown.Admonition("warning", "Almost there!", [text])) # ╔═╡ 0afab53c-0970-11eb-3e43-834513e4632e still_missing(text=md"Replace `missing` with your answer.") = Markdown.MD(Markdown.Admonition("warning", "Here we go!", [text])) # ╔═╡ 0b21c93a-0970-11eb-33b0-550a39ba0843 keep_working(text=md"The answer is not quite right.") = Markdown.MD(Markdown.Admonition("danger", "Keep working on it!", [text])) # ╔═╡ 0b470eb6-0970-11eb-182f-7dfb4662f827 yays = [md"Fantastic!", md"Splendid!", md"Great!", md"Yay ❤", md"Great! 🎉", md"Well done!", md"Keep it up!", md"Good job!", md"Awesome!", md"You got the right answer!", md"Let's move on to the next section."] # ╔═╡ 0b6b27ec-0970-11eb-20c2-89515ee3ab88 correct(text=rand(yays)) = Markdown.MD(Markdown.Admonition("correct", "Got it!", [text])) # ╔═╡ ec576da8-0a2c-11eb-1f7b-43dec5f6e4e7 let # we need to call Base.:+ instead of + to make Pluto understand what's going on # oops if @isdefined(Coordinate) result = Base.:+(Coordinate(3,4), Coordinate(10,10)) if result isa Missing still_missing() elseif !(result isa Coordinate) keep_working(md"Make sure that your return a `Coordinate`. 🧭") elseif result.x != 13 || result.y != 14 keep_working() else correct() end end end # ╔═╡ 0b901714-0970-11eb-0b6a-ebe739db8037 not_defined(variable_name) = Markdown.MD(Markdown.Admonition("danger", "Oopsie!", [md"Make sure that you define a variable called **$(Markdown.Code(string(variable_name)))**"])) # ╔═╡ 66663fcc-0a58-11eb-3568-c1f990c75bf2 if !@isdefined(origin) not_defined(:origin) else let if origin isa Missing still_missing() elseif !(origin isa Coordinate) keep_working(md"Make sure that `origin` is a `Coordinate`.") else if origin == Coordinate(0,0) correct() else keep_working() end end end end # ╔═╡ ad1253f8-0a34-11eb-265e-fffda9b6473f if !@isdefined(make_tuple) not_defined(:make_tuple) else let result = make_tuple(Coordinate(2,1)) if result isa Missing still_missing() elseif !(result isa Tuple) keep_working(md"Make sure that you return a `Tuple`, like so: `return (1, 2)`.") else if result == (2,1) correct() else keep_working() end end end end # ╔═╡ 058e3f84-0a34-11eb-3f87-7118f14e107b if !@isdefined(trajectory) not_defined(:trajectory) else let c = Coordinate(8,8) t = trajectory(c, 100) if t isa Missing still_missing() elseif !(t isa Vector) keep_working(md"Make sure that you return a `Vector`.") elseif !(all(x -> isa(x, Coordinate), t)) keep_working(md"Make sure that you return a `Vector` of `Coordinate`s.") else if length(t) != 100 almost(md"Make sure that you return `n` elements.") elseif 1 < length(Set(t)) < 90 correct() else keep_working(md"Are you sure that you chose each step randomly?") end end end end # ╔═╡ 4fac0f36-0a59-11eb-03d0-632dc9db063a if !@isdefined(initialize) not_defined(:initialize) else let N = 200 result = initialize(N, 1) if result isa Missing still_missing() elseif !(result isa Vector) || length(result) != N keep_working(md"Make sure that you return a `Vector` of length `N`.") elseif any(e -> !(e isa Agent), result) keep_working(md"Make sure that you return a `Vector` of `Agent`s.") elseif length(Set(result)) != N keep_working(md"Make sure that you create `N` **new** `Agent`s. Do not repeat the same agent multiple times.") elseif sum(a -> a.status == S, result) == N-1 && sum(a -> a.status == I, result) == 1 if 8 <= length(Set(a.position for a in result)) <= 9 correct() else keep_working(md"The coordinates are not correctly sampled within the box.") end else keep_working(md"`N-1` agents should be Susceptible, 1 should be Infectious.") end end end # ╔═╡ d5cb6b2c-0a66-11eb-1aff-41d0e502d5e5 bigbreak = html"



"; # ╔═╡ fcafe15a-0a66-11eb-3ed7-3f8bbb8f5809 bigbreak # ╔═╡ ed2d616c-0a66-11eb-1839-edf8d15cf82a bigbreak # ╔═╡ e84e0944-0a66-11eb-12d3-e12ae10f39a6 bigbreak # ╔═╡ e0baf75a-0a66-11eb-0562-938b64a473ac bigbreak # ╔═╡ Cell order: # ╟─19fe1ee8-0970-11eb-2a0d-7d25e7d773c6 # ╟─1bba5552-0970-11eb-1b9a-87eeee0ecc36 # ╟─49567f8e-09a2-11eb-34c1-bb5c0b642fe8 # ╟─181e156c-0970-11eb-0b77-49b143cc0fc0 # ╠═1f299cc6-0970-11eb-195b-3f951f92ceeb # ╟─2848996c-0970-11eb-19eb-c719d797c322 # ╠═2b37ca3a-0970-11eb-3c3d-4f788b411d1a # ╠═2dcb18d0-0970-11eb-048a-c1734c6db842 # ╟─69d12414-0952-11eb-213d-2f9e13e4b418 # ╟─fcafe15a-0a66-11eb-3ed7-3f8bbb8f5809 # ╟─3e54848a-0954-11eb-3948-f9d7f07f5e23 # ╟─3e623454-0954-11eb-03f9-79c873d069a0 # ╠═0ebd35c8-0972-11eb-2e67-698fd2d311d2 # ╟─027a5f48-0a44-11eb-1fbf-a94d02d0b8e3 # ╠═b2f90634-0a68-11eb-1618-0b42f956b5a7 # ╟─66663fcc-0a58-11eb-3568-c1f990c75bf2 # ╟─3e858990-0954-11eb-3d10-d10175d8ca1c # ╠═189bafac-0972-11eb-1893-094691b2073c # ╠═ad1253f8-0a34-11eb-265e-fffda9b6473f # ╟─73ed1384-0a29-11eb-06bd-d3c441b8a5fc # ╠═96707ef0-0a29-11eb-1a3e-6bcdfb7897eb # ╠═b0337d24-0a29-11eb-1fab-876a87c0973f # ╟─9c9f53b2-09ea-11eb-0cda-639764250cee # ╠═e24d5796-0a68-11eb-23bb-d55d206f3c40 # ╠═ec8e4daa-0a2c-11eb-20e1-c5957e1feba3 # ╟─e144e9d0-0a2d-11eb-016e-0b79eba4b2bb # ╠═ec576da8-0a2c-11eb-1f7b-43dec5f6e4e7 # ╟─71c358d8-0a2f-11eb-29e1-57ff1915e84a # ╠═5278e232-0972-11eb-19ff-a1a195127297 # ╟─71c9788c-0aeb-11eb-28d2-8dcc3f6abacd # ╠═69151ce6-0aeb-11eb-3a53-290ba46add96 # ╟─3eb46664-0954-11eb-31d8-d9c0b74cf62b # ╠═edf86a0e-0a68-11eb-2ad3-dbf020037019 # ╠═44107808-096c-11eb-013f-7b79a90aaac8 # ╟─87ea0868-0a35-11eb-0ea8-63e27d8eda6e # ╟─058e3f84-0a34-11eb-3f87-7118f14e107b # ╠═478309f4-0a31-11eb-08ea-ade1755f53e0 # ╠═51788e8e-0a31-11eb-027e-fd9b0dc716b5 # ╟─3ebd436c-0954-11eb-170d-1d468e2c7a37 # ╠═dcefc6fe-0a3f-11eb-2a96-ddf9c0891873 # ╟─b4d5da4a-09a0-11eb-1949-a5807c11c76c # ╠═0237ebac-0a69-11eb-2272-35ea4e845d84 # ╠═ad832360-0a40-11eb-2857-e7f0350f3b12 # ╟─b4ed2362-09a0-11eb-0be9-99c91623b28f # ╠═0665aa3e-0a69-11eb-2b5d-cd718e3c7432 # ╟─ed2d616c-0a66-11eb-1839-edf8d15cf82a # ╟─3ed06c80-0954-11eb-3aee-69e4ccdc4f9d # ╠═35537320-0a47-11eb-12b3-931310f18dec # ╠═cf2f3b98-09a0-11eb-032a-49cc8c15e89c # ╟─814e888a-0954-11eb-02e5-0964c7410d30 # ╠═0cfae7ba-0a69-11eb-3690-d973d70e47f4 # ╠═1d0f8eb4-0a46-11eb-38e7-63ecbadbfa20 # ╟─4fac0f36-0a59-11eb-03d0-632dc9db063a # ╠═e0b0880c-0a47-11eb-0db2-f760bbbf9c11 # ╠═b5a88504-0a47-11eb-0eda-f125d419e909 # ╠═87a4cdaa-0a5a-11eb-2a5e-cfaf30e942ca # ╟─49fa8092-0a43-11eb-0ba9-65785ac6a42f # ╠═1ccc961e-0a69-11eb-392b-915be07ef38d # ╠═1f96c80a-0a46-11eb-0690-f51c60e57c3f # ╟─f953e06e-099f-11eb-3549-73f59fed8132 # ╠═e6dd8258-0a4b-11eb-24cb-fd5b3554381b # ╠═de88b530-0a4b-11eb-05f7-85171594a8e8 # ╟─80f39140-0aef-11eb-21f7-b788c5eab5c9 # ╠═d1bcd5c4-0a4b-11eb-1218-7531e367a7ff # ╟─34778744-0a5f-11eb-22b6-abe8b8fc34fd # ╠═24fe0f1a-0a69-11eb-29fe-5fb6cbf281b8 # ╟─1fc3271e-0a45-11eb-0e8d-0fd355f5846b # ╟─18552c36-0a4d-11eb-19a0-d7d26897af36 # ╠═4e7fd58a-0a62-11eb-1596-c717e0845bd5 # ╠═778c2490-0a62-11eb-2a6c-e7fab01c6822 # ╟─e964c7f0-0a61-11eb-1782-0b728fab1db0 # ╠═4d83dbd0-0a63-11eb-0bdc-757f0e721221 # ╠═ef27de84-0a63-11eb-177f-2197439374c5 # ╟─8475baf0-0a63-11eb-1207-23f789d00802 # ╟─201a3810-0a45-11eb-0ac9-a90419d0b723 # ╠═e5040c9e-0a65-11eb-0f45-270ab8161871 # ╟─f9b9e242-0a53-11eb-0c6a-4d9985ef1687 # ╟─2031246c-0a45-11eb-18d3-573f336044bf # ╠═63dd9478-0a45-11eb-2340-6d3d00f9bb5f # ╠═269955e4-0a46-11eb-02cc-1946dc918bfa # ╠═4d4548fe-0a66-11eb-375a-9313dc6c423d # ╟─20477a78-0a45-11eb-39d7-93918212a8bc # ╠═601f4f54-0a45-11eb-3d6c-6b9ec75c6d4a # ╠═b1b1afda-0a66-11eb-2988-752405815f95 # ╟─e84e0944-0a66-11eb-12d3-e12ae10f39a6 # ╟─05c80a0c-09a0-11eb-04dc-f97e306f1603 # ╟─b53d5608-0a41-11eb-2325-016636a22f71 # ╠═1b5e72c6-0a42-11eb-3884-a377c72270c7 # ╟─c704ea4c-0aec-11eb-2f2c-859c954aa520 # ╠═e97e39aa-0a5d-11eb-3d5f-f90a0acfe5a2 # ╟─b554b654-0a41-11eb-0e0d-e57ff68ced33 # ╠═40c1c1d6-0a69-11eb-3913-59e9b9ec4332 # ╟─18ac9926-0aed-11eb-034f-e9849b71c9ac # ╟─b56ba420-0a41-11eb-266c-719d39580fa9 # ╠═465e918a-0a69-11eb-1b59-01150b4b0f36 # ╟─a885bf78-0a5c-11eb-2383-9d74c8765847 # ╠═1f172700-0a42-11eb-353b-87c0039788bd # ╟─b59de26c-0a41-11eb-2c67-b5f3c7780c91 # ╠═faec52a8-0a60-11eb-082a-f5787b09d88c # ╟─b5b4d834-0a41-11eb-1b18-1bd626d18934 # ╠═a83c96e2-0a5a-11eb-0e58-15b5dda7d2d2 # ╟─05fc5634-09a0-11eb-038e-53d63c3edaf2 # ╠═24c2fb0c-0a42-11eb-1a1a-f1246f3420ff # ╟─c7649966-0a41-11eb-3a3a-57363cea7b06 # ╠═2635b574-0a42-11eb-1daa-971b2596ce44 # ╟─c77b085e-0a41-11eb-2fcb-534238cd3c49 # ╠═274fe006-0a42-11eb-1869-29193bb84957 # ╟─c792374a-0a41-11eb-1e5b-89d9de2cf1f9 # ╠═d147f7f0-0a66-11eb-2877-2bc6680e396d # ╟─e0baf75a-0a66-11eb-0562-938b64a473ac # ╟─0e6b60f6-0970-11eb-0485-636624a0f9d7 # ╟─0a82a274-0970-11eb-20a2-1f590be0e576 # ╟─0aa666dc-0970-11eb-2568-99a6340c5ebd # ╟─0acaf3b2-0970-11eb-1d98-bf9a718deaee # ╟─0afab53c-0970-11eb-3e43-834513e4632e # ╟─0b21c93a-0970-11eb-33b0-550a39ba0843 # ╟─0b470eb6-0970-11eb-182f-7dfb4662f827 # ╟─0b6b27ec-0970-11eb-20c2-89515ee3ab88 # ╟─0b901714-0970-11eb-0b6a-ebe739db8037 # ╟─d5cb6b2c-0a66-11eb-1aff-41d0e502d5e5