This page assumes you have installed everything, and will show you by example all the steps required to create your own complex network simulation. The example shown here is quite basic, to show how to use the code. Some more example code can be found in the /examples/ folder included when you download ComplexNetworkSim.
The example we will use is the spread of a disease in a network of people. For this, we have agents connected in a network. Each agent can have a state (for the simple simulations, a state will be an integer number - other types of state are also possible later on). This state in our case is either healthy but susceptible to a disease, or infected, or recovered and immune.
#define three constants for our example:
SUSCEPTIBLE = 0
INFECTED = 1
RECOVERED = 2
We wish to make an agent who acts depending on their state, which in pseudocode looks like this:
for every timestep:
if SUSCEPTIBLE:
for each infected neighbour:
become infected at a certain probability
else if INFECTED:
wait until recovery happens a few days later
In actual code, this is perhaps the first time slightly strange, and looks like the following (I strongly urge you to become familiar with SimPy first, then this will make more sense)
from ComplexNetworkSim import NetworkAgent, Sim
class SIRSimple(NetworkAgent):
""" an implementation of an agent following the simple SIR model """
def __init__(self, state, initialiser):
NetworkAgent.__init__(self, state, initialiser)
self.infection_probability = 0.05 # 5% chance
self.infection_end = 5
def Run(self):
while True:
if self.state == SUSCEPTIBLE:
self.maybeBecomeInfected()
yield Sim.hold, self, NetworkAgent.TIMESTEP_DEFAULT #wait a step
elif self.state == INFECTED:
yield Sim.hold, self, self.infection_end #wait end of infection
self.state = RECOVERED
yield Sim.passivate, self #remove agent from event queue
def maybeBecomeInfected(self):
infected_neighbours = self.getNeighbouringAgentsIter(state=INFECTED)
for neighbour in infected_neighbours:
if SIRSimple.r.random() < self.infection_probability:
self.state = INFECTED
break
This is where there is a lot of scope for more complex behaviour, and also it’s possible for each individual agent to adhere to slightly or vastly different bahaviour to other agents.
In any case, your own agent must adhere to the following structure, in its constructor calling its superclass constructor and having a Run method:
from PyComplexSimCore import NetworkAgent, Sim
class myAgent(NetworkAgent):
def __init__(self, state, initialiser):
NetworkAgent.__init__(self, 0, initialiser)
#other code goes here
def Run(self):
#further initialisation code may go here
while True:
#main agent logic goes here
The network part is simple, we just use networkx to give us a graph object:
import networkx as nx
nodes = 30 #we want a graph with 30 agents as a test.
# Network and initial states of agents
G = nx.scale_free_graph(nodes)
states = [SUSCEPTIBLE for n in G.nodes()] #list of states corresponding to agent states
Now we can infect one initial disease-spreading agent by initially stating e.g.
states[0] = INFECTED
However there are other, smarter ways of having more control over where an infection starts (see e.g. examples/SIR_model/environment_SIR.py) - but let’s stick to the simple case for now.
Now we defined how our agents should be connected, and what they should do. It’s possible to have changing network structures (temporal complex networks), more on this later. Let’s simulate what happens for our example:
We define an output directory where results are saved, how long we wish to simulate for, the number of simulation trials (the idea is that we can simulate a scenario multiple times with same inputs then take an average over what happens, if that is relevant to our particular case). Then we create an instance of NetworkSimulation with our parameters (more parameters for more complex simulations are possible here, this is just the basics) and call its .runSimulation() method. Easy! :)
from ComplexNetworkSim import NetworkSimulation
# Simulation constants
MAX_SIMULATION_TIME = 25.0
TRIALS = 2
def main():
directory = 'test' #output directory
# run simulation with parameters
# - complex network structure
# - initial state list
# - agent behaviour class
# - output directory
# - maximum simulation time
# - number of trials
simulation = NetworkSimulation(G,
states,
SIRSimple,
directory,
MAX_SIMULATION_TIME,
TRIALS)
simulation.runSimulation()
Now you could run your first simulation by adding then executing the script.
if __name__ == '__main__':
main()
This will produce a few files in the specified output directory in Python pickled format - the results will be apparent when you visualise them (see below). I recommend one directory per simulation! Multiple different simulation outputs in the same directory may cause problems.
So you created a simulation, it runs and produces some pickled output files. Now you wish to visualise the resuts? ComplexNetworkSim has support to build basic plots, and visualise the network state changes over time.
First we need to load the classes:
from ComplexNetworkSim import PlotCreator, AnimationCreator
Then we can define a few parameters for the names:
directory = 'test' #location of simulation result files
myName = "SIR" #name that you wish to give your image output files
title = "Simulation of agent-based simple SIR"
#define three simulation-specific constants:
SUSCEPTIBLE = 0
INFECTED = 1
RECOVERED = 2
Next we define a few parameters for what states we wish to show on the plot, along with their legend label and colour:
statesToMonitor = [INFECTED, SUSCEPTIBLE] #even if we have states 0,1,2,3,... plot only 1 and 0
colours = ["r", "g"] #state 1 in red, state 0 in green
labels = ["Infected", "Susceptible"] #state 1 named 'Infected', 0 named 'Susceptible'
Next we define a colour for the animated nodes depending on their state. Let’s make suceptible white, infected red and recovered 40% gray. Also, probably we generated multiple trial runs - the plot will take an average of all of them, but the animation will follow a single trial, so we can optionally specify it (default=trial 0):
mapping = {SUSCEPTIBLE:"w", INFECTED:"r", RECOVERED:"0.4"}
trialToVisualise = 0
Now for the actual call to the plotting and animating, we simply create an instance of the PlotCreator class with the defined parameters and call its .plotSimulation() method:
p = PlotCreator(directory, myName, title, statesToMonitor, colours, labels)
p.plotSimulation(show=False)
#show=True shows the graph directly,
#otherwise only a png file is created in the directory defined above.
Similarly for the visualisation; create an instance of the AnimationCreator class and call .create_gif. If ImageMagick is installed, it will create PNG image files for each time step plus an animated gif. If it is not installed, it will still create the PNGs and warn you that ImageMagick is not installed.
visualiser = AnimationCreator(directory, myName, title, mapping, trial=trialToVisualise)
#gif speed can be changed by giving a parameter 'delay' (default=100) to AnimationCreator
visualiser.create_gif(verbose=True)
And that’s it! Running this code may take several seconds, because it will create multiple image files - but the good thing is this speed won’t worsen with a more complicated simulation. Have a look at the generated output in the defined output directory. The listed code on this page can also be found in the file plotAndAnimate.py within your /examples folder.
You can try to run the provided example models (examples folder) like any other Python script. I suggest having a look at them before writing your own models. You may also wish to become familiar with both SimPy and NetworkX before attempting to write you own complex network simulation models, as this project is based heavily on those libraries.
If you wish to use another way of visualising the simulation output, you can have a look at the classes for plotting.py, animation.py and statistics.py to see how to handle the output, and then create your own way of visualising things from the simulation output.
Quick link to Acknowledgements and further information