Start both clocks:
and wait 6 seconds
Run superclock:
Reached 0.0 ticks per second
0
UndefVarError: trigger not defined
Here is what happened, the most recent locations are first:
3
homework 6, version 0
Submission by: Jazzy Doe (jazz@mit.edu)
Oopsie! You need to update Pluto to the latest version
Close Pluto, go to the REPL, and type:
julia> import Pkg
julia> Pkg.update("Pluto")
Homework 6: Epidemic modeling III
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!
"Jazzy Doe"
"jazz"
Let's create a package environment:
Activating new project at `/tmp/jl_bAU2nb` Updating registry at `~/.julia/registries/General.toml` Resolving package versions... Installed PlutoUI ─ v0.6.11 Updating `/tmp/jl_bAU2nb/Project.toml` [91a5bcdd] + Plots v1.40.10 [7f904dfe] + PlutoUI v0.6.11 Updating `/tmp/jl_bAU2nb/Manifest.toml` [66dad0bd] + AliasTables v1.1.3 [d1d4a3ce] + BitFlags v0.1.9 [d360d2e6] + ChainRulesCore v1.25.1 [9e997f8a] + ChangesOfVariables v0.1.9 [944b1d66] + CodecZlib v0.7.8 [35d6a980] + ColorSchemes v3.29.0 [3da002f7] + ColorTypes v0.12.0 [c3611d14] + ColorVectorSpace v0.11.0 [5ae59095] + Colors v0.13.0 [34da2185] + Compat v4.16.0 [f0e56b4a] + ConcurrentUtilities v2.5.0 [187b0558] + ConstructionBase v1.5.8 [d38c429a] + Contour v0.6.3 [9a962f9c] + DataAPI v1.16.0 [864edb3b] + DataStructures v0.18.20 [ffbed154] + DocStringExtensions v0.9.3 [460bff9d] + ExceptionUnwrapping v0.1.11 [c87230d0] + FFMPEG v0.4.2 [53c48c17] + FixedPointNumbers v0.8.5 [1fa38f19] + Format v1.3.7 [28b8d3ca] + GR v0.73.6 [42e2da0e] + Grisu v1.0.2 [cd3eb016] + HTTP v1.10.15 [3587e190] + InverseFunctions v0.1.17 [92d709cd] + IrrationalConstants v0.2.4 [1019f520] + JLFzf v0.1.9 [692b3bcd] + JLLWrappers v1.7.0 [682c06a0] + JSON v0.21.4 [b964fa9f] + LaTeXStrings v1.4.0 [23fbe1c1] + Latexify v0.16.6 [2ab3a3ac] + LogExpFunctions v0.3.28 [e6f89c97] + LoggingExtras v1.1.0 [1914dd2f] + MacroTools v0.5.15 [739be429] + MbedTLS v1.1.9 [442fdcdd] + Measures v0.3.2 [e1d29d7a] + Missings v1.2.0 [77ba4419] + NaNMath v1.0.3 [4d8831e6] + OpenSSL v1.4.3 [bac558e1] + OrderedCollections v1.8.0 [69de0a69] + Parsers v2.8.1 [b98c9c47] + Pipe v1.3.0 [ccf2f8ad] + PlotThemes v3.3.0 [995b91a9] + PlotUtils v1.4.3 [91a5bcdd] + Plots v1.40.10 [7f904dfe] + PlutoUI v0.6.11 [aea7be01] + PrecompileTools v1.2.1 [21216c6a] + Preferences v1.4.3 [43287f4e] + PtrArrays v1.3.0 [3cdcf5f2] + RecipesBase v1.3.4 [01d81517] + RecipesPipeline v0.6.12 [189a3867] + Reexport v1.2.2 [05181044] + RelocatableFolders v1.0.1 [ae029012] + Requires v1.3.1 [6c6a2e73] + Scratch v1.2.1 [992d4aef] + Showoff v1.0.3 [777ac1f9] + SimpleBufferStream v1.2.0 [a2af1166] + SortingAlgorithms v1.2.1 [860ef19b] + StableRNGs v1.0.2 [82ae8749] + StatsAPI v1.7.0 [2913bbd2] + StatsBase v0.34.4 [fd094767] + Suppressor v0.2.8 [62fd8b95] + TensorCore v0.1.1 [3bb67fe8] + TranscodingStreams v0.11.3 [5c2747f8] + URIs v1.5.1 [1cfade01] + UnicodeFun v0.4.1 [1986cc42] + Unitful v1.22.0 [45397f5d] + UnitfulLatexify v1.6.4 [41fe7b60] + Unzip v0.2.0 [6e34b625] + Bzip2_jll v1.0.9+0 [83423d85] + Cairo_jll v1.18.2+1 [ee1fde0b] + Dbus_jll v1.14.10+0 [2702e6a9] + EpollShim_jll v0.0.20230411+1 [2e619515] + Expat_jll v2.6.5+0 [b22a6f82] + FFMPEG_jll v4.4.4+1 [a3f928ae] + Fontconfig_jll v2.15.0+0 [d7e528f0] + FreeType2_jll v2.13.3+1 [559328eb] + FriBidi_jll v1.0.16+0 [0656b61e] + GLFW_jll v3.4.0+2 [d2c73de3] + GR_jll v0.73.6+0 [78b55507] + Gettext_jll v0.21.0+0 [7746bdde] + Glib_jll v2.82.4+0 [3b182d85] + Graphite2_jll v1.3.14+1 [2e76f6c2] + HarfBuzz_jll v8.5.0+0 [aacddb02] + JpegTurbo_jll v3.1.1+0 [c1c5ebd0] + LAME_jll v3.100.2+0 [88015f11] + LERC_jll v3.0.0+1 [1d63c593] + LLVMOpenMP_jll v18.1.7+0 [dd4b983a] + LZO_jll v2.10.3+0 [e9f186c6] + Libffi_jll v3.2.2+2 [d4300ac3] + Libgcrypt_jll v1.11.0+0 [7e76a0d4] + Libglvnd_jll v1.7.0+0 [7add5ba3] + Libgpg_error_jll v1.51.1+0 [94ce4f54] + Libiconv_jll v1.18.0+0 [4b2f31a3] + Libmount_jll v2.40.3+0 [89763e89] + Libtiff_jll v4.5.1+1 [38a345b3] + Libuuid_jll v2.40.3+0 [e7412a2a] + Ogg_jll v1.3.5+1 [458c3c95] + OpenSSL_jll v3.0.16+0 [91d4177d] + Opus_jll v1.3.3+0 [36c8627f] + Pango_jll v1.56.1+0 [30392449] + Pixman_jll v0.43.4+0 [c0090381] + Qt6Base_jll v6.7.1+1 [a44049a8] + Vulkan_Loader_jll v1.3.243+0 [a2964d1f] + Wayland_jll v1.21.0+2 [2381bf8a] + Wayland_protocols_jll v1.36.0+0 [02c8fc9c] + XML2_jll v2.13.6+1 [aed1982a] + XSLT_jll v1.1.42+0 [ffd25f8a] + XZ_jll v5.6.4+1 [f67eecfb] + Xorg_libICE_jll v1.1.1+0 [c834827a] + Xorg_libSM_jll v1.2.4+0 [4f6342f7] + Xorg_libX11_jll v1.8.6+3 [0c0b7dd1] + Xorg_libXau_jll v1.0.12+0 [935fb764] + Xorg_libXcursor_jll v1.2.3+0 [a3789734] + Xorg_libXdmcp_jll v1.1.5+0 [1082639a] + Xorg_libXext_jll v1.3.6+3 [d091e8ba] + Xorg_libXfixes_jll v6.0.0+0 [a51aa0fd] + Xorg_libXi_jll v1.8.2+0 [d1454406] + Xorg_libXinerama_jll v1.1.5+0 [ec84b674] + Xorg_libXrandr_jll v1.5.4+0 [ea2f1a96] + Xorg_libXrender_jll v0.9.11+1 [14d82f49] + Xorg_libpthread_stubs_jll v0.1.2+0 [c7cfdc94] + Xorg_libxcb_jll v1.17.0+3 [cc61e674] + Xorg_libxkbfile_jll v1.1.2+1 [e920d4aa] + Xorg_xcb_util_cursor_jll v0.1.4+0 [12413925] + Xorg_xcb_util_image_jll v0.4.0+1 [2def613f] + Xorg_xcb_util_jll v0.4.0+1 [975044d2] + Xorg_xcb_util_keysyms_jll v0.4.0+1 [0d47668e] + Xorg_xcb_util_renderutil_jll v0.3.9+1 [c22f9ab0] + Xorg_xcb_util_wm_jll v0.4.1+1 [35661453] + Xorg_xkbcomp_jll v1.4.6+1 [33bec58e] + Xorg_xkeyboard_config_jll v2.39.0+0 [c5fb5394] + Xorg_xtrans_jll v1.5.1+0 [3161d3a3] + Zstd_jll v1.5.7+1 [35ca27e7] + eudev_jll v3.2.9+0 [214eeab7] + fzf_jll v0.56.3+0 [1a1c6b14] + gperf_jll v3.1.1+1 [a4ae2306] + libaom_jll v3.11.0+0 [0ac62f75] + libass_jll v0.15.2+0 [1183f4f0] + libdecor_jll v0.2.2+0 [2db6ffa8] + libevdev_jll v1.11.0+0 [f638f0a6] + libfdk_aac_jll v2.0.3+0 [36db933b] + libinput_jll v1.18.0+0 [b53b4c65] + libpng_jll v1.6.47+0 [f27f6e37] + libvorbis_jll v1.3.7+2 [009596ad] + mtdev_jll v1.1.6+0 [1270edf5] + x264_jll v2021.5.5+0 [dfaa095f] + x265_jll v3.5.0+0 [d8fb68d0] + xkbcommon_jll v1.4.1+2 [0dad84c5] + ArgTools [56f22d72] + Artifacts [2a0f44e3] + Base64 [ade2ca70] + Dates [8bb1440f] + DelimitedFiles [f43a241f] + Downloads [7b1f6079] + FileWatching [b77e0a4c] + InteractiveUtils [b27032c2] + LibCURL [76f85450] + LibGit2 [8f399da3] + Libdl [37e2e46d] + LinearAlgebra [56ddb016] + Logging [d6f4376e] + Markdown [a63ad114] + Mmap [ca575930] + NetworkOptions [44cfe95a] + Pkg [de0858da] + Printf [3fa0cd96] + REPL [9a3f8284] + Random [ea8e919c] + SHA [9e88b42a] + Serialization [6462fe0b] + Sockets [2f01184e] + SparseArrays [10745b16] + Statistics [fa267f1f] + TOML [a4e569a6] + Tar [8dfed614] + Test [cf7118a7] + UUIDs [4ec0a83e] + Unicode [e66e0078] + CompilerSupportLibraries_jll [deac9b47] + LibCURL_jll [29816b5a] + LibSSH2_jll [c8ffd9c3] + MbedTLS_jll [14a3606d] + MozillaCACerts_jll [4536629a] + OpenBLAS_jll [05823500] + OpenLibm_jll [efcefdf7] + PCRE2_jll [83775a58] + Zlib_jll [8e850b90] + libblastrampoline_jll [8e850ede] + nghttp2_jll [3f19e933] + p7zip_jll Precompiling project... ✓ PlutoUI 1 dependency successfully precompiled in 2 seconds (155 already precompiled)
We began this module with data on the COVID-19 epidemic, but then looked at mathematical models. How can we make the connection between data and models?
Models have parameters, such as the rate of recovery from infection. Where do the parameter values come from? Ideally we would like to extract them from data. The goal of this homework is to do this by fitting a model to data.
For simplicity we will use data that generated from the spatial model in Homework 5, rather than real-world data, and we will fit the simplest SIR model. But the same ideas apply more generally.
There are many ways to fit a function to data, but all must involve some form of optimization, usually minimization of a particular function, a loss function; this is the basis of the vast field of machine learning.
The loss function is a function of the model parameters; it measures how far the model output is from the data, for the given values of the parameters.
We emphasise that this material is pedagogical; there is no suggestion that these specific techniques should be used actual calculations; rather, it is the underlying ideas that are important.
Exercise 1: Calculus without calculus
Before we jump in to simulating the SIR equations, let's experiment with a simple 1D function. In calculus, we learn techniques for differentiating and integrating symbolic equations, e.g.
Instead, we use ✨ computers ✨ to approximate derivatives and integrals. Instead of applying rules to symbolic expressions, we use much simpler strategies that only use the output values of our function.
As a first example, we will approximate the derivative of a function. Our method is inspired by the analytical definition of the derivative!
The finite difference method simply fixes a small value for
Exercise 1.1 - tangent line
👉 Write a function finite_difference_slope
that takes a function f
and numbers a
and h
. It returns the slope
finite_difference_slope (generic function with 2 methods)
0.2
Got it!
Let's move on to the next section.
👉 Write a function tangent_line
that takes the same arguments f
, a
and g
, but it returns a function. This function (finite_difference_slope
) that passes through
Hint
Remember that functions are objects! For example, here is a function that returns the square root function:
function the_square_root_function()
f = x -> sqrt(x)
return f
end
tangent_line (generic function with 1 method)
Got it!
Great! 🎉
The slider below controls
Notice that, as you decrease
0.31622776601683794
Exercise 1.2 - antiderivative
In the finite differences method, we approximated the derivative of a function:
We can do something very similar to approximate the 'antiderivate' of a function. Finding the antiderivative means that we use the slope
This antiderivative problem is illustrated below. The only information that we have is the slope at any point
Using only this information, we want to reconstruct
By rearranging the equation above, we get the Euler method:
Using this formula, we only need to know the value
👉 Write a function euler_integrate_step
that applies this formula to a known function
15
euler_integrate_step (generic function with 1 method)
1
1
Got it!
Good job!
👉 Write a function euler_integrate
that takes takes a known function T
with a == first(T)
and h == step(T)
. It applies the function euler_integrate_step
repeatedly, once per entry in T
, to produce the sequence of values
euler_integrate (generic function with 1 method)
Let's try it out on T
ranging from
We already know the analytical solution 0.0
to 1000.0
.
0.003
0.015
0.042
0.09
0.165
0.273
0.42
0.612
0.855
1.155
1.518
1.95
2.457
3.045
3.72
4.488
5.355
6.327
7.41
8.61
791.43
817.377
843.885
870.96
898.608
926.835
955.647
985.05
1015.05
1045.65
Got it!
Keep it up!
You see that our numerical antiderivate is not very accurate, but we can get a smaller error by choosing a smaller step size. Try it out!
There are also alternative integration methods that are more accurate with the same step size. Some methods also use the second derivative, other methods use multiple steps at once, etc.! This is the study of Numerical Methods.
Exercise 2: Simulating the SIR differential equations
Recall from the lectures that the ordinary differential equations (ODEs) for the SIR model are as follows:
where
We will use the simplest possible method to simulate these, namely the Euler method. The Euler method is not always a good method to solve ODEs accurately, but for our purposes it is good enough.
In the previous exercise, we introduced the euler method for a 1D function, which you can see as an ODE that only depends on time. For the SIR equations, we have an ODE that only depends on the previous value, not on time, and we have 3 equations instead of 1.
The solution is quite simple, we apply the euler method to each of the differential equations within a single time step to get new values for each of
👉 Implement a function euler_SIR_step(β, γ, sir_0, h)
that performs a single Euler step for these equations with the given parameter values and initial values, with a step size
sir_0
is a 3-element vector, and you should return a new 3-element vector with the values after the timestep.
euler_SIR_step (generic function with 1 method)
0.989901
0.010049
5.0e-5
👉 Implement a function euler_SIR(β, γ, sir_0, T)
that applies the previously defined function over a time range
You should return a vector of vectors: a 3-element vector for each point in time.
euler_SIR (generic function with 1 method)
0.0:0.1:60.0
0.989703
0.010147
0.00015
0.989402
0.0102961
0.000302205
0.989096
0.0104472
0.000456646
0.988786
0.0106005
0.000613355
0.988472
0.010756
0.000772363
0.988153
0.0109136
0.000933702
0.987829
0.0110734
0.00109741
0.987501
0.0112355
0.00126351
0.987168
0.0113998
0.00143204
0.986831
0.0115664
0.00160304
0.986488
0.0117353
0.00177653
0.986141
0.0119066
0.00195256
0.985789
0.0120802
0.00213116
0.985431
0.0122563
0.00231236
0.985069
0.0124348
0.00249621
0.984702
0.0126157
0.00268273
0.984329
0.0127992
0.00287197
0.983951
0.0129852
0.00306396
0.983568
0.0131737
0.00325873
0.983179
0.0133648
0.00345634
0.218585
0.0274236
0.753991
0.218405
0.0271921
0.754403
0.218227
0.0269624
0.75481
0.218051
0.0267344
0.755215
0.217876
0.0265083
0.755616
0.217703
0.0262839
0.756013
0.217531
0.0260614
0.756408
0.217361
0.0258405
0.756799
0.217192
0.0256214
0.757186
0.217025
0.025404
0.757571
Let's plot
plot_sir! (generic function with 1 method)
👉 Do you see an epidemic outbreak (i.e. a rapid growth in number of infected individuals, followed by a decline)? What happens after a long time? Does everybody get infected?
blabla
👉 Make an interactive visualization in which you vary
Exercise 3: Numerical gradient
For fitting we need optimization, and for optimization we will use derivatives (rates of change). In Exercise 1, we wrote a function finite_difference_slope(f, a)
to approximate
Exercise 3.1
👉 Write functions ∂x(f, a, b)
and ∂y(f, a, b)
that calculate the partial derivatives
Recall that
You should use anonymous functions for this. These have the form x -> x^2
, meaning "the function that sends
∂x (generic function with 1 method)
42.00699999999813
Got it!
You got the right answer!
∂y (generic function with 1 method)
1.0000000000047748
Got it!
Well done!
Exercise 3.2
👉 Write a function gradient(f, a, b)
that calculates the gradient of a function
gradient (generic function with 1 method)
42.007
1.0
Got it!
Well done!
Exercise 4: Minimisation using gradient descent
In this exercise we will use gradient descent to find local minima of (smooth enough) functions.
To do so we will think of a function as a hill. To find a minimum we should "roll down the hill".
Exercise 4.1
We want to minimize a 1D function, i.e. a function
👉 Write a function gradient_descent_1d_step(f, x0)
that performs a single gradient descrent step, from the point x0
and using your function finite_difference_slope
to approximate the derivative. The result should be the next guess for
gradient_descent_1d_step (generic function with 1 method)
4.899989999999974
Got it!
Let's move on to the next section.
gradient_1d_viz (generic function with 1 method)
👉 Write a function gradient_descent_1d(f, x0)
that repeatedly applies the previous function (N_steps
times), starting from the point x0
, like in the vizualisation above. The result should be the final guess for
gradient_descent_1d (generic function with 1 method)
4.999499991586009
Got it!
Awesome!
Right now we take a fixed number of steps, even if the minimum is found quickly. What would be a better way to decide when to end the function?
blabla
Exericse 4.2
Multivariable calculus tells us that the gradient
👉 Write functions gradient_descent_2d_step(f, x0, y0)
and gradient_descent_2d(f, x0, y0)
that do the same for functions
gradient_descent_2d_step (generic function with 1 method)
gradient_descent_2d (generic function with 1 method)
2.99957
1.99976
himmelbau (generic function with 1 method)
We also prepared a 3D visualisation if you like! It's a bit slow...
false
gradient_2d_viz_3d (generic function with 1 method)
gradient_2d_viz_2d (generic function with 1 method)
👉 Can you find different minima?
Exercise 5: Learning parameter values
In this exercise we will apply gradient descent to fit a simple function
To do so we need to define what "best" means. We will define a measure of the distance between the function and the data, given by a loss function, which itself depends on the values of
The iterative procedure by which we gradually adjust the parameter values to improve the loss function is often called machine learning or just learning, since the computer is "discovering" information in a gradual way, which is supposed to remind us of how humans learn. [Hint: This is not how humans learn.]
Exercise 5.1 - 🎲 frequencies
We generate a small dataset by throwing 10 dice, and counting the sum. We repeat this experiment many times, giving us a frequency distribution in a familiar shape.
dice_frequencies (generic function with 1 method)
10:60
0.0
0.0
0.0
0.0
0.0
0.0
0.0001
0.0002
0.0004
0.0
Let's try to fit a gaussian (normal) distribution. Its PDF with mean
👉 (Not graded) Manually fit a Gaussian distribution to our data by adjusting
μ =
σ =
23.0
4.795831523312719
4.79583
4.79583
Show manual fit:
gauss (generic function with 1 method)
What we just did was adjusting the function parameters until we found the best possible fit. Let's automate this process! To do so, we need to quantify how good or bad a fit is.
👉 Define a loss function to measure the "distance" between the actual data and the function. It will depend on the values of
loss_dice (generic function with 1 method)
false
👉 Use your gradient_descent_2d
function to find a local minimum of found_μ
and found_σ
.
34.9776
5.48048
Go back to the graph to see your optimized gaussian curve!
If your fit is close, then probability theory tells us that the found parameter
0.031535469100603564
0.0698042507869081
Got it!
Great!
35.00915
5.410671518166677
Exercise 6: Putting it all together — fitting an SIR model to data
In this exercise we will fit the (non-spatial) SIR ODE model from Exercise 1 to some data generated from the spatial model in Problem Set 4. If we are able to find a good fit, that would suggest that the spatial aspect "does not matter" too much for the dynamics of these models. If the fit is not so good, perhaps there is an important effect of space. (As usual in statistics, and indeed in modelling in general, we should be very cautious of making claims of this nature.)
This fitting procedure will be different from that in Exercise 4, however: we no longer have an explicit form for the function that we are fitting – rather, it is the output of an ODE! So what should we do?
We will try to find the parameters
Exercise 6.1
Below the result from Homework 4, Exercise 3.2. These are the average S, I, R fractions of running 20 simulations. Click on it!
1:1000
0.99
0.01
0.0
0.9895
0.0105
0.0
0.9895
0.0105
0.0
0.989
0.011
0.0
0.9885
0.0115
0.0
0.9885
0.0115
0.0
0.9885
0.0115
0.0
0.988
0.0115
0.0005
0.9865
0.013
0.0005
0.986
0.0135
0.0005
0.9855
0.014
0.0005
0.9855
0.014
0.0005
0.9855
0.014
0.0005
0.9845
0.015
0.0005
0.9845
0.015
0.0005
0.9845
0.015
0.0005
0.984
0.0155
0.0005
0.984
0.0155
0.0005
0.984
0.0155
0.0005
0.9835
0.016
0.0005
0.05
0.2335
0.7165
0.05
0.2335
0.7165
0.05
0.233
0.717
0.05
0.233
0.717
0.05
0.2325
0.7175
0.05
0.232
0.718
0.05
0.2315
0.7185
0.05
0.231
0.719
0.05
0.2295
0.7205
0.05
0.2285
0.7215
👉 (Not graded) Manually fit the SIR curves to our data by adjusting
β =
γ =
Show manual fit:
👉 To do this automatically, we will again need to define a loss function
This time, instead of comparing two vectors of numbers, we need to compare two vectors of vectors, the S, I, R values.
loss_sir (generic function with 1 method)
350.1798977340281
👉 Use this loss function to find the optimal parameters
0.0185516
0.00162125
Got it!
If you made it this far, congratulations – you have just taken your first step into the exciting world of scientific machine learning!
Before you submit
Remember to fill in your name and Kerberos ID at the top of this notebook.
cannot assign a value to variable PlutoUI.as_svg from module workspace#3
Here is what happened, the most recent locations are first:
- from run_expression.jl:92
- evalfrom boot.jl:373
as_mime (generic function with 1 method)
Function library
Just some helper functions used in the notebook.
hint (generic function with 1 method)
almost (generic function with 1 method)
still_missing (generic function with 2 methods)
keep_working (generic function with 2 methods)
Fantastic!
Splendid!
Great!
Yay ❤
Great! 🎉
Well done!
Keep it up!
Good job!
Awesome!
You got the right answer!
Let's move on to the next section.
correct (generic function with 2 methods)
not_defined (generic function with 1 method)