05
Programming one CTMC event
We now combine the total rate, exponential waiting time, conditional event selection and biological state change to produce exactly one complete continuous-time transition.
The unique purpose of this lesson
This page completes this mechanism once. It does not repeat the mechanism into a trajectory; that loop is the unique purpose of Lesson 6.
Scenario
At current time \(t=12\) days, use:
The current event rates are:
for infection, progression and recovery, respectively. The total rate is \(a_0=2.985\) events/day.
Use two independent random numbers
Waiting-time draw
\(U_1\) determines when the event occurs.
Event-selection draw
\(U_2\) determines which event occurs.
Use separate independent draws. Reusing the same uniform number would create an artificial relationship between waiting time and event type that is not part of the CTMC model.
Advance continuous time
The current state remains unchanged throughout the waiting period. At its end, the selected event occurs instantaneously:
Unlike a DTMC, this increment is not a fixed \(\Delta t\). Every CTMC event generally produces a different positive waiting time.
Select the event from rate ratios
The cumulative conditional boundaries are:
- If \(U_2<c_1\), select infection.
- If \(c_1\le U_2<c_2\), select progression.
- Otherwise, select recovery.
There is no no-change choice because waiting has already been represented by \(\tau\).
Apply exactly one state update
Infection
Progression
Recovery
The event rate controls selection, but the selected event controls the integer-valued compartment changes.
Do not update while calculating rates
All rates must be calculated from the same current state. Only after both random decisions have been made should the program form the next state.
Using next_S, next_E, next_I and next_R keeps the current and next states separate and avoids mixing values from different times.
What if no event is possible?
If \(a_0=0\), the current state is absorbing:
- there is no finite waiting time;
- no event type can be normalised or selected; and
- the state remains unchanged indefinitely.
A robust function should detect this case before calculating \(-\log(U_1)/a_0\).
Validation after the transition
Interactive Python laboratory
Click Run code to perform one complete event. The result shows rates, two random numbers, waiting time, selected event, event time and before/after state.
Output
Run the code to see the result.
Understand the combined code
| Code | Purpose |
|---|---|
rates.sum() | Calculates the current total rate. |
u_time = 1.0 - rng.random() | Generates a positive uniform value for the logarithm. |
u_event = rng.random() | Uses a separate random draw for event selection. |
next_time = time + waiting_time | Advances continuous time by the generated waiting duration. |
next_S, next_E, next_I, next_R = ... | Assigns the complete next state in the selected branch. |
float("inf") | Represents indefinite waiting when no transition rate is positive. |
Read the result correctly
The generated waiting time tells how long the process remains in the current state. At the reported next-event time, the selected event changes the state by one allowed transition.
Running the code with another seed can change both the waiting time and event type. Neither result alone is an average prediction; each is one valid random CTMC event.
Try these experiments
- Change the seed and observe that the two random draws and resulting event change.
- Set \(E=0\) while keeping \(I>0\). Confirm that progression probability becomes zero.
- Use \((S,E,I,R)=(990,10,0,0)\). Progression remains possible even when infection and recovery rates are zero.
- Use \((S,E,I,R)=(990,0,0,10)\). Confirm that the function reports an absorbing state rather than dividing by zero.
- Temporarily reuse
u_timeasu_event, then restore the independent draw and explain why coupling them is inappropriate.
What this lesson has—and has not—done
You can now:
- calculate all rates from one current state;
- generate one exponential waiting time;
- select one event using an independent uniform draw;
- advance continuous time and update the state;
- handle absorbing states; and
- validate population counts and time direction.
Not yet: the mechanism has been executed only once. Lesson 6 repeats it, recalculates rates after every jump and stores a complete Gillespie trajectory.