Discrete-event simulation

epydemic is basically a discrete-event simulator with a mixed population of events.

The basics of discrete-event simulation

Discrete-event simulation represents a process as a sequence of events happening in sequence. Each event happens at a specific instant in time, and may give rise to further events to happen later. The job of the simulator is to progress simulated time forward and execute the events in the correct temporal order – even though they may be have been generated in some other order.

For example, suppose we have a sequence of events A, B, C, with A scheduled to happen before B, and B before C. The execution of A might given rise to two other events D and E where D occurs before B and E occurs after B but before C. After executing A, the future event order then becomes D, B, E, C, and the simulator will execute D next.

Clearly one cannot meaningfully create an event that should execute before the current simulation time.

Event implementation

Events are implemented in epydemic simply as methods on a Process object that are called when the event “fires”. An event is associated with a node or edge in the network, and can then perform any arbitrary function relating to that network element. (Examples include changing the compartments of nodes, or re-wiring edges.)

Events can also be named, which both provides a useful hook for debugging and explanation, and is a key part of the Event taps sub-system.

Posted and stochastic events

epydemic supports two kinds of events: posted and stochastic.

A posted event is an event that happens at a specific, known, simulation time. that is to say, both the event and the time when it will happen are known when the event is created. Events are posted by calling Dynamics:postEvent().

A stochastic event is an event chosen using a stochastic process, meaning that the event happens (or doesn’t) on some model element (typically a node or edge). Events are declared using Dynamics.addFixedRateEventr() or Dynamics.addEventPerElement(). Exactly how these events are chosen depend on the dynamics used (see below).

Decomposition

Different parts of the code are responsible for different aspects of the simulation.

The Dynamics class abstracts the functions of the simulator. It maintains an event queue containing all posted events, the loci for stochastic events, and the event probabilities that apply to those loci. Taken together, these form the distribution for stochastic events.

Each dynamics class has an associated Process instance which build the process in terms of loci and event probabilities, and defines the event functions. The SIR process, for example, has two events types (infection and removal). The probability of these events occuring is a function of the number of SI edges and the number of I nodes (two loci), and the disease dynamics specified when the process is created.

The logic of this decomposition is that the Dynamics holds all the information relating to a single simulation run. The Process sets up the simulation and provides the logic for how events are handled. This makes epydemic more flexible in two key respects: it can support diffrent simulation dynamics using the same process definition (see below), and it can combine processes together to form composite processes. Neither of these two options would be available if we (for example) defined a process by sub-classing the simulation dynamics directly.

Event taps

As well as evaluating an event function, the event stream is “tapped” to a single well-known method, Dynamics.eventFired(). This allows sub-classes of Dynamics to act on every event, regardless of which process defines it and whether it was stochastic or posted.

When events are defined or posted using the various methods, they can optionally be given names. These names are then passed to Dynamics.eventFired(), and form the only way in which different types of events can be differentiated.

This feature isn’t used at all within “core” epydemic, but the appropriate calls are in place for all the core classes (for example StochasticDynamics). It is provided as another extension mechanism that can be used if and when needed.

Note also that event taps are defined intimately tied-up with the Process class, and need one to operate. This means they’re not defined for classes derived directly from NetworkExperiment (which includes BondPercolation and SitePercolation).