# How Epidemics Spread 9, Exercise 3 # Exercise 3: combining a contact cut with case isolation. R0 = 2.5 (assumed); a contact cut c gives R = 2.5 * (1 - c). import math import random def poisson(lam): """Poisson random number (Knuth's method; fine for small lam).""" limit, k, p = math.exp(-lam), 0, 1.0 while True: p *= random.random() if p <= limit: return k k += 1 def escaped(R, detect_mean, cap=300, seed_cases=3): """Isolation only (no tracing): True if the outbreak reaches `cap` cases.""" queue = [(0.0, random.gammavariate(4, detect_mean / 4)) for _ in range(seed_cases)] total = 0 while queue: t, iso = queue.pop() total += 1 if total >= cap: return True for _ in range(poisson(R)): child_t = t + random.gammavariate(4, 1.25) if child_t < iso: queue.append((child_t, child_t + random.gammavariate(4, detect_mean / 4))) return False random.seed(33) runs = 200 print("contact cut R after cut escape % (detect 7 d) (detect 5 d) (detect 3 d)") for c in (0.0, 0.2, 0.4, 0.5): R = 2.5 * (1 - c) row = [sum(escaped(R, d) for _ in range(runs)) / runs * 100 for d in (7, 5, 3)] print(f"{c*100:9.0f}% {R:13.2f} {row[0]:21.0f} {row[1]:14.0f} {row[2]:14.0f}") # Output when run: # contact cut R after cut escape % (detect 7 d) (detect 5 d) (detect 3 d) # 0% 2.50 96 68 0 # 20% 2.00 78 12 0 # 40% 1.50 19 0 0 # 50% 1.25 0 0 0 # Reading the output: a contact cut and quicker isolation work together because both lower the number of people each case infects. With no contact cut, detection after 7 days leaves 96 per cent of outbreaks escaping, 5 days 68 per cent, and 3 days none in these 200 runs. A 40 per cent cut with 7-day detection brings escape down to 19 per cent, and a 50 per cent cut stops it at 0. With 3-day detection nothing escapes even without a cut, in this model. The model is optimistic: every case is detected and isolation is perfect. The values are assumed.