Goal. Reproduce a Moran process by hand so the birth-death dynamics and fixation are felt before they are formalised. This activity is deliberately longer than the usual 20 minutes.
Use moran process form and have students play in pairs.
Also requires use of dice, allowing for multiples of 4 and 6. For example:
Virtual modification: use breakout rooms of 4. For dice, show how to use the
Python random library.
Explain that we will aim to reproduce a Moran process with \(n=3\).

We will do this with the Hawk–Dove game:
Recall, this corresponds to sharing of 4 resources:
Give students 5 minutes to write out the fitnesses of each type in each possible situation.
Confirm:
| \(f(\text{hawk})\) | \(f(\text{dove})\) | |
|---|---|---|
| 1 hawk, 2 doves | \(0\times0 + 2\times3 = 6\) | \(1\times1 + 1\times2 = 3\) |
| 2 hawks, 1 dove | \(1\times0 + 1\times3 = 3\) | \(2\times1 + 0\times2 = 2\) |
Give students 5 minutes to write out the probabilities of selection of each type in each possible situation.
Confirm:
| Select | Selection: birth | Selection: death | |
|---|---|---|---|
| 1 hawk, 2 doves | hawk | \(\frac{6}{6 + 2\times3}\) | \(\frac{1}{3}\) |
| 1 hawk, 2 doves | dove | \(\frac{2\times3}{6 + 2\times3}\) | \(\frac{2}{3}\) |
| 2 hawks, 1 dove | hawk | \(\frac{2\times3}{2\times3 + 2}\) | \(\frac{2}{3}\) |
| 2 hawks, 1 dove | dove | \(\frac{2}{2\times3 + 2}\) | \(\frac{1}{3}\) |
Let students simulate.
Count from groups to obtain mean fixation rate of a hawk.
Show the following code that allows us to simulate the simulation undertaken by the students (this is not the same code as in the notes):
import collections
import matplotlib.pyplot as plt
import numpy as np
import tqdm
def roll_n_sided_dice(n=6):
"""Roll a dice with n sides."""
return np.random.randint(1, n + 1)
class MoranProcess:
"""
A class for a Moran process with a population of size n=3 using the
standard hawk-dove game:
A = [[0, 3],
[1, 2]]
Note that this is a simulation corresponding to an in-class activity
where students roll dice.
"""
def __init__(self, number_of_hawks=1, seed=None):
if seed is not None:
np.random.seed(seed)
self.number_of_hawks = number_of_hawks
self.number_of_doves = 3 - number_of_hawks
self.dice_and_values_for_hawk_birth = {1: (6, {1, 2, 3}), 2: (4, {1, 2, 3})}
self.dice_and_values_for_hawk_death = {1: (6, {1, 2}), 2: (6, {1, 2, 3, 4})}
self.history = [(self.number_of_hawks, self.number_of_doves)]
def step(self):
"""Select a hawk or a dove for birth and death, then update."""
birth_dice, birth_values = self.dice_and_values_for_hawk_birth[self.number_of_hawks]
death_dice, death_values = self.dice_and_values_for_hawk_death[self.number_of_hawks]
select_hawk_for_birth = self.roll_dice_for_selection(dice=birth_dice, values=birth_values)
select_hawk_for_death = self.roll_dice_for_selection(dice=death_dice, values=death_values)
if select_hawk_for_birth:
self.number_of_hawks += 1
else:
self.number_of_doves += 1
if select_hawk_for_death:
self.number_of_hawks -= 1
else:
self.number_of_doves -= 1
self.history.append((self.number_of_hawks, self.number_of_doves))
def roll_dice_for_selection(self, dice, values):
"""Return True if the roll is in the target set."""
return roll_n_sided_dice(n=dice) in values
def simulate(self):
"""Run the simulation until the number of hawks is 0 or 3."""
while self.number_of_hawks in [1, 2]:
self.step()
return self.number_of_hawks
def __len__(self):
return len(self.history)
This carries out the simulations:
repetitions = 10 ** 5
end_states = []
path_lengths = []
for seed in range(repetitions):
mp = MoranProcess(seed=seed)
end_states.append(mp.simulate())
path_lengths.append(len(mp))
counts = collections.Counter(end_states)
counts[3] / repetitions
# ≈ 0.54666
Discuss obtaining theoretic probabilities of changing state:
Now work through the notes, culminating in the proof of the theorem for the absorption probabilities of a birth–death process.
Discuss and use code from the chapter to show the fixation with the hawk–dove game:
a = np.array([[0, 3], [1, 2]])
Calculate theoretic value using the formula from the theorem:
This gives (for \(n=3\)):
| \(i=1\) | \(i=2\) | |
|---|---|---|
| \(f_{1i}\) | 3 | 3/2 |
| \(f_{2i}\) | 3/2 | 1 |
| \(\gamma_i\) | 1/2 | 2/3 |
Thus:
Work through the Moran Process chapter.
Discussion Point: After the definition of the moran process, ask how this differs from our example?
Discussion Point: After the definition of fixation probability, ask what the fixation probability of a hawk is based on our simulation?
Discussion Point: After the theoretic fixation in two types ask how this could be used for our example? Leave this as an exercise.
The activity above is written up as a marked exam question: Question 1 (the in-class activity) on the Moran Process page, with a full worked solution. Closing the loop here is the step that helps students who find exams hard: work through that question together, or set it as the immediate follow-up, so they see the game they just played turned into a full-mark answer.
General email templates to send before and after this class. Fill in the bracketed placeholders before sending.
Hi all,
A reminder that our next Game Theory class covers Moran Processes.
All of the course materials, including the relevant chapter, are available
at https://vknight.org/gt/. It is worth skimming the chapter beforehand.
See you in class,
Vince
Dear all,
Thanks for your work in today's class on Moran Processes.
A recording is available here [RECORDING LINK] and on Learning Central.
All class resources are available at https://vknight.org/gt/.
Thanks,
Vince