# How Epidemics Spread 9, Exercise 2 # Exercise 2: how much tracing, how fast? R = 2.5, detection mean 5 days, 3 seed cases (all assumed). 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, p_trace, trace_delay, cap=300, seed_cases=3): """True if the outbreak reaches `cap` cases before dying out.""" 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: continue child_iso = child_t + random.gammavariate(4, detect_mean / 4) if random.random() < p_trace: child_iso = min(child_iso, max(child_t, iso + trace_delay)) queue.append((child_t, child_iso)) return False random.seed(21) runs = 200 print("tracing coverage delay 1 day delay 3 days delay 5 days (% of outbreaks that escape)") for p in (0.3, 0.5, 0.7, 0.9): row = [] for delay in (1, 3, 5): esc = sum(escaped(2.5, 5, p, delay) for _ in range(runs)) row.append(esc / runs * 100) print(f"{p*100:15.0f}% {row[0]:13.0f} {row[1]:14.0f} {row[2]:14.0f}") # Output when run: # tracing coverage delay 1 day delay 3 days delay 5 days (% of outbreaks that escape) # 30% 27 51 66 # 50% 2 39 58 # 70% 0 19 55 # 90% 0 2 55 # Reading the output: two things matter, how many contacts are traced and how fast. At a 1-day delay, 50 per cent coverage is already enough to bring escape down to about 2 per cent, but at 30 per cent coverage 27 per cent of outbreaks still escape. With a 5-day delay, which is as slow as detection itself, even 90 per cent coverage leaves 55 per cent escaping, hardly better than isolating cases alone (59 per cent in the chapter, from a different set of 300 runs). With 200 runs per cell, differences of a few points are noise (about 3 points either way at 50 per cent, my rough estimate from the binomial spread). The numbers come from a simplified model with assumed values, not a forecast for any disease.