PHYS 410 - Tutorial 7: Random Walks & Monte Carlo Integration The goal of this tutorial is to model a random walk in two dimensions and observe a phase transition as parameters are varied. Additionally, some Monte Carlo integration exercises are included at the end for practice. 1. Zombie Apocalypse Rumour has it that mad scientists from UBC have been working on a dangerous virus that turns anyone it infects into a zombie. Alarmed, the City of Vancouver has contacted you because of your expertise in producing fast and reliable simulations. Their request is simple: they want you to model the potential consequences of a zombie invasion and to find ways to mitigate the damages it would cause to the population. The setup The simplest model of a zombie invasion is that of a two-dimensional random walk. For the sake of the argument, we will assume that both zombies and terrified humans walk randomly on a Cartesian lattice, and that the infection is transmitted when walkers find themselves on the same site as a zombie. We will also assume that the virus is so potent that zombies eventually die of organ failure, and thus stop infecting humans. An effective simulation therefore needs to keep track of the health status of all walkers on the lattice at each time step, updating it as necessary. In order to assess the consequences of a pandemic outbreak, many parameters have to be taken into account. Among those are the density of the population, the survival time of a zombie, the probability of transmitting the disease, and the fraction of people immune to the virus, to name only a few. Definitions Luckily for you, the officials at City Hall already have an incomplete version of a zombie invasion code. In it, they define the following parameters: a grid size N, such that the lattice possesses N N sites; a density variable that specifies the fraction of sites occupied by people; a time T which measures the number of iterations (hours) it takes for a zombie to die; a number of walker M, expressed in terms of N and density. 1
The advantage of vectorization Intuitively, it would make sense to code the zombie invasion with a series of embedded for and if loops that cover the realm of all possibilities for the walkers: do we take a step in x? in y? does the infection get transmitted? and so forth. However, coding that way is extremely inefficient. The City of Vancouver is in a state of emergency and time is a limited resource, so you need to find a way to get results quickly. Since MATLAB excels at manipulating matrices and vectors, the best solution is to minimize the quantity of loops, which act on individual elements, in favour of operations that act on arrays all at once. This technique is called vectorization. 1 Note: In a vectorized random walk, all walkers take random steps simultaneously. A subtle problem caused by this is that neighbours can never find themselves on the same site after a random step, which implies that zombies can never infect adjacent walkers. An easy fix is to infect the walkers who are on the same site as a zombie as well as those who step on a site where a zombie used to be at the previous iteration. Question 1: Initialization To initialize the random walk, the first step is to randomly assign positions to all M walkers. To keep track of the location of every walker, we can define two vectors x and y with the statement randi(n, M, 1), which creates a column vector whose M elements take random integer values between 1 and N. Thus, walker j has position {x(j), y(j)} on the lattice. The second step is to turn a random walker into a zombie. After this step, we need to be able to distinguish between healthy, infected, and dead walkers. To do so, let us initialize a vector, which we will call infect and whose elements take either the value 0 (healthy), 1 (zombie), or 2 (dead). Therefore, we would like infect to be 0 everywhere except at the position of the infected walker, where it takes the value 1. Remember that zombies can only wander around for T hours before dying, which means that we also have to keep track of the time that passes after every infection. An easy way to do so is through a vector hours initialized to the value T for everyone, but which decreases every iteration for all zombies. When an element hours(j) reaches 0, we will need to update infect(j) from 1 to 2 (where 2 means death; the walker stops moving altogether). Additionally, we will initialize three counters to keep track of casualties and time: ndeaths = 0, nzombies = 1, and iter = 1. 1 On my 2012 MacBook Pro, a simulation at high population density constructed of loops takes more than 200 seconds to run, compared to 1 second on the vectorized version of the same code. 2
Question 2: Random walk The random walk will take place in a while loop that keeps on running as long as the conditions nzombies > 0 and iter < Nitermax are met. To walk randomly, we need random directions. Convince yourself that the statement >> d e l t a x = 2 floor (2 rand) 1 ; where rand is a number randomly chosen in the open interval (0, 1), defines a variable deltax which exclusively takes the values -1 or 1. As mentioned earlier, the walkers in a vectorized random walk all take steps simultaneously. Look at the Random Walk part of the incomplete code, and add a section for the random walk in the y direction. Be sure to understand how to use array-wide statements like deltax(infect == 2) = 0, or what type of variable walkx and walky are, as knowing this is the key to eschew the use of loops. Notice how some problems might arise in xnew if walkers take steps outside of the N N lattice. Implement periodic boundary conditions in both the x and y directions to prevent walkers from leaving the simulation. Question 3: Infection and death counts There are a few tasks your code needs to do before each random step: If a walker is infected, subtract 1 from its hours counter; Update the status of all zombies who die on this iteration; Update the counters nzombies and ndeaths. Do not use if loops! Use logical operations instead, as in Question 2. For instance, the number of dead walkers is simply ndeaths = sum(infect == 2) in vectorized notation. Question 4: Contamination The contamination step occurs on two separate occasions: a healthy walker and a zombie land on the same site; a walker steps on a site that a zombie just left. We need to compare the x and y coordinates of the healthy walkers to those of the infected ones and, if there are matches, proceed with contamination. The statement C = ismember(a, B) compares the arrays A and B and outputs a logical array C taking the value 1 for all elements of A also found in B, and 0 for the unique ones. 3
Therefore, all that you need to do is to define a variable called I coord which contains the x and y coordinates (old and new) of the infected zombies. Also, make sure to understand why H coord has been initialized the way it has. Question 5: Additional modifications Make sure that your code works properly. When it does, add these two features to make it more realistic: There is a possibility that some people are naturally immune to the virus. Assume that a fraction pimmune of the population (chosen at random) cannot be contaminated, and modify the initialization part of your code to take this into account. Hint: in the case of immunization, inf ect should take an additional value. Some viruses are more contagious than others. Add a probability of transmission that determines whether contamination occurs or not. The variable infection status should be modified accordingly. Hint: The health status of walkers corresponds to the binary true/infected (1) and false/healthy (0). Therefore, a simple way to implement contagion is to make infection status a logical array obtained from a logical comparison to a transmission probability. If k walkers have been in contact with zombies after some iteration, then infection status should be a logical vector of length k where each element determines whether one of these walker randomly becomes a zombie or not. Question 6: Critical phenomena Now that you have a working model of a zombie invasion, you can answer some questions that the mayor Gregor Robertson has for you: Given a density of 0.5 and a transmission probability of 0.8, what is the minimum value of pimmune needed so that most people stay alive? What if the density is 0.25? Given a density of 0.5 and assuming that 30% of the population is immune against the virus, how contagious does the virus need to be in order to become pandemic? How would these results change if the zombies died after 12 hours (instead of 24)? You should observe critical phenomena as you play with various parameters. Indeed, there seems to be a phase transition between the two population states mostly alive and mostly dead. (1) Given the random nature of your simulation, you should run your code many times with the same parameters and average your results. How would you estimate the error associated with your predictions? 4
2. Monte Carlo Integration Question 1: Estimating π using Monte Carlo Explain why the following lines calculate π: >> k = 1 e7 ; >> p i e s t = 4 sum(sum(rand(k,2).ˆ2,2) <=1)/ k ; Question 2: Hyperspheres Monte Carlo integration shines the most in higher dimensional applications. Use the logic of Question 1 to calculate the volume inside S d, the d-sphere, defined by for d = 2, i.e. for a ball in R 3 ; for d = 5. d+1 x 2 i = 1, (2) i=1 Question 3: Importance sampling Compute the integral I = by using Monte Carlo integration with Uniform sampling; 1 0 ( x 1/3 + x ) dx (3) 10 Importance sampling with the sampling function w(x) = 2 3 x 1/3. The actual answer is 1.55. Plot the error for k going from 10 2 to 10 6 on a loglog plot; what do you observe? Question 4: Importance sampling (again) Repeat the above exercice with the integral I = 1 0 e x dx (4) with the sampling function w(x) = 1 + x, since e x 1 + x on the interval (0, 1). 5