06
Loops for a complete epidemic trajectory
One transition gives only the next state. We now repeatedly use that one-step rule, store every state and construct one realised epidemic path through time.
The unique purpose of this lesson
A trajectory, also called a sample path or realisation, is one sequence of states produced by the stochastic model:
Lessons 3–5 already explained the probabilities, random intervals and one-state update. The genuinely new programming ideas here are repetition, storage and stopping.
One trajectory is not a probability distribution and is not the average of all possible epidemics. Another random seed can produce a different valid path.
Scenario: follow one outbreak until it ends
Consider a closed SIR population of 200 people:
Use \(\beta=0.3\) per day, \(\gamma=0.1\) per day and fixed step \(\Delta t=0.01\) day. We will follow the population until either:
- \(I=0\), meaning that infection has become extinct; or
- 160 days have been simulated.
From one transition to a trajectory
The essential relationship is:
The next state from one iteration becomes the current state for the following iteration.
Why store the initial state before the loop?
times = [0.0]
S_values = [S]
I_values = [I]
R_values = [R]
The lists begin with \(X_0\), the state before any transition occurs. The loop then appends \(X_1,X_2,\ldots\). If the initial state were omitted, the plotted path would incorrectly begin after the first transition.
The loop structure
for step in range(max_steps):
result = one_sir_transition(...)
S, I, R = result["next_state"]
times.append((step + 1) * dt)
S_values.append(S)
I_values.append(I)
R_values.append(R)
if I == 0:
break
for and range
range(max_steps) supplies the integers from 0 to max_steps - 1. The loop body runs once for each supplied integer unless break stops it early.
State replacement
S, I, R = result["next_state"] makes the newly produced state the current state for the next iteration.
append
append adds one new value to the end of a list. All four lists grow together and remain aligned by time step.
break
When \(I=0\), break immediately leaves the loop. Further steps would only repeat the absorbing disease-free state.
Why use step + 1 for time?
Python begins range(max_steps) at step = 0. However, the first state produced inside the loop is \(X_1\), occurring at time \(\Delta t\), not at time zero.
Therefore the time stored after the first transition is:
(step + 1) * dt
This avoids an off-by-one error, where states are attached to the wrong time values.
Stopping rules and their meanings
Biological stopping rule
Stop when I == 0. In this closed SIR model, infection cannot restart because there is no importation and recovered people do not return to susceptibility.
Computational stopping rule
Stop after max_steps even if infection remains. This guarantees that the program finishes and defines the observation window.
If the time horizon is reached while \(I>0\), we cannot say that extinction occurred. We can say only that infection was still present at the end of the chosen observation period.
Choosing a valid number of steps
If the final time is \(T\) and the fixed step is \(\Delta t\), then:
max_steps = int(max_days / dt)
int converts the result into a whole number because range requires an integer. Here \(160/0.01=16000\) possible steps.
Interactive Python laboratory
Click Run code to generate one complete stochastic trajectory. The result includes a summary, selected rows from the stored table, event counts and a step plot.
Output
Run the code to see the result.
How to read the output table
| Column | Meaning |
|---|---|
time | The model time attached to the stored state. |
S, I, R | The integer compartment counts at that time. |
event | The event that produced this row from the preceding row. |
The first row is labelled initial state because no transition produced it. Every later row records infection, recovery or no change.
Why use a step plot?
A DTMC state remains at its recorded value until the next fixed observation time, then may jump to another integer state. Therefore:
plt.step(..., where="post")
is more faithful than joining the values with sloping lines. A sloping line could visually suggest that fractional people change continuously between time points.
Understand the new code
| Code | Meaning |
|---|---|
for step in range(max_steps) | Allows at most max_steps repetitions of the one-transition rule. |
times.append(time) | Adds the new time to the end of the time list. |
break | Stops the loop early when the biological stopping condition is met. |
pd.DataFrame({...}) | Combines the aligned lists into a labelled rectangular table. |
.iloc[-1] | Selects the last stored value by its position. |
.idxmax() | Returns the row label at which the infectious count first reaches its maximum. |
.value_counts() | Counts how many times each event label occurs. |
.all() | Checks whether a condition is true for every value being tested. |
Three validations after the loop
The one-step function protects each transition. The checks after the loop protect the complete stored dataset.
Try these experiments
- Change
seed = 7. Compare the peak, final state and stopping time with the original trajectory. - Use
seed = 2. Does early randomness produce a noticeably different path? - Change
max_days = 20. If \(I>0\) at the end, explain why the result is not an extinction time. - Start with
S, I, R = 199, 1, 0. The outbreak may disappear very early or grow. - Comment out the
breakstatement. Observe that the disease-free state is repeatedly stored until the time horizon.
Biological interpretation
The plotted line is one possible history of the epidemic. Early infection and recovery events can change whether the outbreak grows, how high it peaks and when it becomes extinct.
The final removed count is the number of people infected during this particular realisation because everyone begins susceptible or infectious and removed people do not return to susceptibility.
We should not generalise from one path. Estimating extinction probability, expected peak size or the distribution of final epidemic size requires many independent trajectories. That Monte Carlo task is reserved for Lesson 12.
What this lesson has—and has not—done
You can now:
- repeat a one-step DTMC transition with a loop;
- store the initial state and every later state in aligned lists;
- attach states to the correct discrete times;
- stop at extinction or a fixed time horizon;
- validate and tabulate a complete trajectory; and
- display an integer-valued stochastic path with a step plot.
Next: Lessons 7–9 apply this general trajectory machinery to the distinctive biology and state structures of SIS, SIR and SEIR models. Lesson 7 begins with reinfection in an SIS epidemic.