Process
: Base class for network processes
- class epydemic.Process
Bases:
object
A process that runs over a network. This is the abstract base class for all network processes. It provides the essential routines to build, set-up, run, and extract results from a process. Sub-classes provide the actual behaviour by defining simulation events and attaching them to the network in different ways.
A process definition is largely declarative, in the sense that it sets up event handlers and their probabilities. These are used by a process dynamics (a sub-class of
Dynamics
) to actually run a simulation of the process.Processes can be composed into larger structures, for example as part of a
ProcessSequence
.The process class includes an interface for interacting with the working network. The basic interface is extended and overridden in different sub-classes that interact with the network in different ways. The process also provides helper methods to save explicit use of the
Dynamics
when writing events.
Setup and initialisation
Five methods provide the core API for defining new processes, and are typically overridden by sub-classes.
- Process.reset()
Reset the process ready to be built. This resets all the internal process state variables.
- Process.build(params)
Build the process model. This should be overridden by sub-classes, and should create the various elements of the model.
- Parameters:
params (
Dict
[str
,Any
]) – the model parameters
- Process.setUp(params)
Set up the network under the given dynamics. The default does nothing: sub-classes should override it to initialise node states or establish other properties ready for the experiment.
- Parameters:
params (
Dict
[str
,Any
]) – the simulation parameters
- Process.tearDown()
Tear down any structures built for this run of the process. The default does nothing.
- Process.dynamics()
Return the instance of
Dynamics
running this process.- Return type:
- Returns:
the dynamics
- Process.currentSimulationTime()
Return the current simulation time. Only makes sense when called from a running simulation.
- Return type:
float
- Returns:
the time
- Process.results()
Create and return an empty dict to be filled with experimental results. Sub-classes should extend this method to add results to the dict.
- Return type:
Dict
[str
,Any
]- Returns:
an empty dict for experimental results
Process hierarchy
Processes can be nested, for example using a ProcessSequence
.
The component processes can be accessed using a common interface:
Getting ready to run
Several other methods provide information for the process.
- Process.setNetwork(g)
Set the network the process is running over.
- Parameters:
g (
Graph
) – the network
- Process.network()
Return the network the process is running over.
- Return type:
Graph
- Returns:
the network
- Process.setDynamics(d)
Set the instance of
Dynamics
that runs the process.- Parameters:
d (
Dynamics
) – the dynamics
- Process.dynamics()
Return the instance of
Dynamics
running this process.- Return type:
- Returns:
the dynamics
- Process.setMaximumTime(t)
Set the maximum default simulation time. The default is given by
DEFAULT_MAX_TIME
. This is used byatEquilibrium()
as the default way to determine equilibrium.The maximum time may be slightly exceeded due to the ways in which events are drawn.
Setting the maximum time persists across runs of the process, and isn’t reset to its default by calling
reset()
.- Parameters:
t (
float
) – the maximum simulation time
- Process.maximumTime()
Return the maximum assumed simulation time.
- Return type:
float
- Returns:
the maximum simulation time
Accessing and evolving the network
A process will generally want to access the working network in the course of its execution,
mainly in event functions. Accessing the network can be done directly, through network()
:
however, processes often need to track changes made to the network, and for this reason the
class provides an interface for evolving the network, paralleling the methods available
in networkx.
The interface may be overridden and extended by sub-classes. Three methods form the general core.
- Process.addNode(n, **kwds)
Add a node to the working network. Any keyword arguments added as node attributes
- Parameters:
n (
Any
) – the new nodekwds – (optional) node attributes
- Process.removeNode(n)
Remove a node from the working network.
- Parameters:
n (
Any
) – the node
- Process.addEdge(n, m, **kwds)
Add an edge between nodes. Any keyword arguments are added as edge attributes. If the endpoint nodes do not already exist then an exception is raised.
- Parameters:
n (
Any
) – the start nodem (
Any
) – the end nodekwds – (optional) edge attributes
- Process.removeEdge(n, m)
Remove an edge from the working network.
- Parameters:
n (
Any
) – the start nodem (
Any
) – the end node
Four other “bulk” methods are deinfed in terms of the basic methods, and so don’t typically need to be overridden specifically.
- Process.addNodesFrom(ns, **kwds)
Add all the nodes in the given iterable to the working network. Any keyword arguments are added as node attributes. This works by calling
addNode()
for each element of the iterable.- Parameters:
ns (
Iterable
[Any
]) – an iterable collection of nodeskwds – (optional) node attributes
- Process.removeNodesFrom(ns)
Remove all the nodes in the given iterable from the working network. This works by iteratively calling
removeNode()
for each element of the iterable collection.- Parameters:
ns (
Iterable
[Any
]) – an iterable collection of nodes
- Process.addEdgesFrom(es, **kwds)
Add all the edges in the given iterable to the working network. Any keyword arguments are added as node attributes. This works by calling
addEdge()
for each element of the iterable.- Parameters:
es (
Iterable
[Tuple
[Any
,Any
]]) – an iterable collection of edgeskwds – (optional) node attributes
- Process.removeEdgesFrom(es)
Remove all the edges in the given iterable collection from the working network. This works by iteratively calling
removeEdge()
for each edge in the iterable.- Parameters:
es (
Iterable
[Tuple
[Any
,Any
]]) – an iterable collection of edges
Loci
Loci are the “locations” at which things happen. The purpose of a Locus
is to keep
track of something – a set of nodes, the entire network, and so forth – so that
simulation can proceed efficiently.
- Process.addLocus(n, l=None)
Add a named locus.
Events
Events are the code fragments that run as part of the simulation. The collection of events defined by a process form all the possible actions that the simulation will perform. Events can be given meaningful names, which don’t affect the execution of the simulation.
There are three broad classes of events. Per-element events occur with a probability on each element of a locis. This means that loci with more elements will generate a higher rate of events.
- Process.addEventPerElement(l, pr, ef, name=None)
Add a probabilistic event at a locus, occurring with a particular (fixed) probability for each element of the locus, and calling the event function when it is selected.
The optional name is used in conjunction with event taps when calling
eventFired()
.- Parameters:
l (
Union
[str
,Locus
]) – the locus or locus namepr (
float
) – the event probabilityef (
Callable
[[float
,Union
[Any
,Tuple
[Any
,Any
]]],None
]) – the event functionname (
Optional
[str
]) – (optional) meaningful name of the event
Fixed-rate events, by contrast, occur with a probability that’s independent of the number of elements in a locus, as long as it’s not empty. This means that the rate at which such events fire is independent of the size of the locus.
- Process.addFixedRateEvent(l, pr, ef, name=None)
Add a probabilistic event at a locus, occurring with a particular (fixed) probability, and calling the event function when it is selected. The locus may be a
Locus
object or a string, which is taken to be the name of a locus of this process. This is a helper method that callsDynamics.addFixedRateEvent()
on the dynamics running the process.Unlike fixed rate events added by
addEventPerElement()
, a fixed probability event happens with the same probability regardless of how many elements are in the locus.- Parameters:
l (
Union
[str
,Locus
]) – the locus or locus namepr (
float
) – the event probabilityef (
Callable
[[float
,Union
[Any
,Tuple
[Any
,Any
]]],None
]) – the event functionname (
Optional
[str
]) – (optional) meaningful name of the event
These two kinds of events are both stochastic, in the sense that they are generated according to an exponential probability distribution.
In contrast, posted events are set to occur at a particular simulation time. As the simulation proceeds it will execute posted events in the correct time sequence relative to the different stochastic events that are generated.
- Process.postEvent(t, e, ef, name=None)
Post an event that calls the event function at time t. This is a helper method that calls
Dynamics.postEvent()
on the dynamics running the process.- Parameters:
t (
float
) – the time to fire the evente (
Any
) – the element (node or edge) on which the event occursef (
Callable
[[float
,Union
[Any
,Tuple
[Any
,Any
]]],None
]) – the event functionname (
Optional
[str
]) – (optional) meaningful name of the event
- Return type:
int
- Process.unpostEvent(id, fatal=True)
Un-post the given event. This is a helper method that calls
Dynamics.postEvent()
on the dynamics running the process.A KeyError will normally be raised if the event is not queued, which typically means it’s been fired already (i.e., its posting time lies in the past relative to the current simulation time): set fatal to False to avoid this.
- Parameters:
id (
int
) – the event idfatal (
bool
) – whether to raise KeyError for missing events (defaults to True)
- Return type:
Optional
[float
]- Returns:
the time for which the event was posted, or None
- Process.pendingEventTime(id)
Return the time for which the given event is posted. This is a helper event trhat calls
Dynamics.pendingEventTime()
. A KeyError will be raised if the event is not queued, which typically means it’s been fired already (i.e., its posting time lies in the past relative to the current simulation time).- Parfam, id:
the event
- Return type:
float
- Returns:
the event’s posted simulation time
- Process.postRepeatingEvent(t, dt, e, ef, name=None)
Post an event that starts at time t and re-occurs at interval dt. This is a help[er methoid that calls
Dynamics.postRepeatingEvent()
on the dynamics running the process.- Parameters:
t (
float
) – the start timedt (
float
) – the intervale (
Any
) – the element (node or edge) on which the event occursef (
Callable
[[float
,Union
[Any
,Tuple
[Any
,Any
]]],None
]) – the element functionname (
Optional
[str
]) – (optional) meaningful name of the event
Event distributions
The stochastic events form probability distributions from which events can be drawn in the course of the simulation. The events are added using the methods above; the distributions are computed automatically.
- Process.perElementEventDistribution(t)
Return the distribution of per-element events at the given time. By default the distribution is time-independent.
Note that this method returns the probability of events, not their expected rates, for which use
perElementEventRateDistribution()
.- Parameters:
t (
float
) – the simulation time- Return type:
List
[Tuple
[Locus
,float
,Callable
[[float
,Union
[Any
,Tuple
[Any
,Any
]]],None
],str
]]- Returns:
a list of (locus, probability, event function) triples
- Process.perElementEventRateDistribution(t)
Return the rates of per-element events at the given time. By default the distribution is time-independent.
Note that this is method returns event rates, not their probabilities as returned by
perElementEventDistribution()
. The rate is simply an event’s probability multiplied by the size of the locus on which it occurs, giving the expected number of events occurring from that locus in unit time.- Parameters:
t (
float
) – the simulation time- Return type:
List
[Tuple
[Locus
,float
,Callable
[[float
,Union
[Any
,Tuple
[Any
,Any
]]],None
],str
]]- Returns:
a list of (locus, rate, event function) triples
- Process.fixedRateEventDistribution(t)
Return the distribution of fixed-rate events at the given time. By default the distribution is time-independent.
- Parameters:
t (
float
) – the simulation time- Return type:
List
[Tuple
[Locus
,float
,Callable
[[float
,Union
[Any
,Tuple
[Any
,Any
]]],None
],str
]]- Returns:
a list of (locus, probability, event function) triples
In some cases it may be necessary to create the distributions directly, in which case these methods can be overridden.
Identifiers for instances, runs, and state
Every process instance has an identifier that’s guaranteed to be unique within this simulation, and a run identifier that’s guaranteed to be unique to different runs of the same process instance. Taken together, these two identifiers uniquely identify a single run of a single process instance.
- Process.instanceId()
Return the unique instance identifier of this process.
- Return type:
int
- Returns:
the instacne id
- Process.runId()
Return the unique run identifier for the current run. This is updated whenever the process is reset by a call to
reset()
.- Return type:
int
- Returns:
the run id
There is also a method for defining “constants” to be used as attributes on nodes and edges for storing process state.
- Process.stateVariable(stem)
Create a unique name for a state variable using the given stem. The default combines the stem with the process instance id.
- Parameters:
stem (
str
) – the name stem- Return type:
str
- Returns:
the state variable name
(See Defining more new processes for an example of how to define state variables.)
Containment
Processes can have a hierarchy, for example when composed into a
ProcessSequence
. There are a couple of methods used to access
this hierarchy.