Process: Base class for network processes
- class epydemic.Process(name=None)
Bases:
objectA 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. They can also be named to allow for multiple instances of the same process within a single simulation.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
Dynamicswhen writing events.- Parameters:
name (
str) – (optional) the instance name
Note
Instance names can be any string: however, it may make other tasks easier if they are a single word without spaces or other characters that Python might become confused by in some contexts, for “First_disease” rather than “First disease” or “First.disease”.
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.
You can access process parameters directly from the model parameters dict, or (better) use
Process.getParameters()to extract them in a single operation.- 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.
You can access process parameters directly from the model parameters dict, or (better) use
Process.getParameters()to extract them in a single operation.- 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
Dynamicsrunning this process.- Return type:
- Returns:
the dynamics
- Process.currentSimulationTime()
Return the current simulation time. Only makes sense when called from a running simulation, for example within an event handler.
- 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
Dynamicsthat runs the process.- Parameters:
d (
Dynamics) – the dynamics
- Process.dynamics()
Return the instance of
Dynamicsrunning 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
Setting and accessing parameters
Added in version 1.14.1: Parameter and result handling were changed in epydemic version
1.14.1 to accommodate using multiple instances of a process within
a single simulation.
The Process.build() and Process.setUp() methods pass
parameters to a process instance. You can access this dict directly,
or (better) access all the relevant parameters in one operation. This
approach works better when there may be multiple instances of a
process, because it automatically takes account of parameters
decorated with instance names.
Note
See the tutorial page on running a simulation for an example using these functions, and the cookbook recipe on co-infection for an example using multiple instances.
- Process.getParameters(params, ks)
Return the parameters of a process.
This should be used in
Process.build()andProcess.setUp()to access the process’ experimental parameters.The parameters are extracted from the parameters dict and returned as a list, with an exception being raised if any undefined parameters are requested. The parameters extracted use
Process.decoratedName()to take account of any instance name assigned to this process. If no decorated parameter is defined, an undecorated parameter with the same underlying name (found usingProcess.undecratedName()will be used.Each parameter can either be a string or a pair of a string and a default value that is returned if the parameter can’t be acquired.
- Parameters:
params (
Dict[str,Any]) – the parametersks (
List[Union[str,Tuple[str,Any]]]) – a list of parameters, optionally with default values
- Return type:
List[Any]- Returns:
the extracted parameters
- Process.setParameters(params, kvs)
Set the parameters.
This method should be used when setting up an experiment. The parameters will be decorated with an instance name.
- Param:
params: the parameters dict
- Params kvs:
the parameter names and values
- Return type:
Dict[str,Any]- Returns:
the updated dict
If necessary you can decorate parameter names manually.
- Process.decoratedNameInInstance(k)
Return the name of a parameter or result name in a specific process instance.
This uses
decoratedName()to decorate the name with any process instance name.- Parameters:
k (
str) – the name name- Return type:
str- Returns:
the decorated parameter name
Storing and accessing results
Added in version 1.14.1: Parameter and result handling were changed in epydemic version
1.14.1 to accommodate using multiple instances of a process within
a single simulation.
As with parameters, results may use decorated names to bind them to a specific process instance.
- Process.setResults(rc, kvs)
Set the results.
This should be used in
Process.results()to add results to the results dict of the process when it completes. The results will be decorated with an instance name.- Param:
rc: the results
- Params kvs:
the result names and values
- Return type:
Dict[str,Any]- Returns:
the updated dict
- Process.getResults(rc, ks)
Return results from an
epycresults dict.This should be used when accessing the results of an experiment, to extract the results that correspond to a particular process instance. It accesses the “results” part of the results dict returned by
epyc.Experiment.run()orepyc.Lab.runExperiment().The parameters are extracted from the parameters dict and returned as a list, with an exception being raised if any undefined parameters are requested. The parameters extracted use
Process.decoratedName()to take account of any instance name assigned to this process. If no decorated parameter is defined, an undecorated parameter with the same underlying name (found usingProcess.undecratedName()will be used.- Parameters:
rc (
Dict[str,Dict[str,Any]]) – the results dictks (
List[str]) – a list of parameters
- Return type:
List[Any]- Returns:
the extracted results
As with parameters, Process.decoratedNameInInstance() will
return the decorated name of any parameter or result. This is useful
when accessing a set of results through pandas.
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
Locusobject 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 processes, runs, and state
Every process 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.uniqueId()
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 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.
- 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.)
Finally, names can be decorated with a process’ instance name.
- Process.decoratedName(k)
Decorate a name with the process’ instance name.
The name is left unchanged if the process does not have an instance name.
This method is used to decorate all names that need to be associated with a sapecific process instance.
- Parameters:
k (
str) – the name- Return type:
str- Returns:
the decorated name.
- Process.undecoratedName(k)
Undecorate a name if it is dcrated with the process’ instance name.
- Parameters:
k (
str) – the name- Return type:
str- Returns:
the undecorated name.
Running multiple instances
You may also want to run several different instances of the same process within a single simulation, for example when exploring co-infection with two diseases having different spreading parameters.
Note
epydemic has supported multiple process instances since version 1.14.1
In this case you can give names to process instances and use these to decorate their parameters.
- Process.instanceName()
Return the instance name of the current process.
- Return type:
str- Returns:
the instance name or None
Containment
Processes can have a hierarchy, for example when composed into a
ProcessSequence. There are a couple of methods used to access
this hierarchy.