Getting started with ComplexNetworkSim

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

Create some agent behaviour

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. By subclassing NetworkAgent, you get a few functions, such as .getNeighbourgingAgents or .getNeighbouringNodes which gives you a list of agent objects or node objects respectively. Agents can also die, create new agents, add and remove links to other agents at runtime, for more complex networked behaviour.

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

Define a complex network

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:

Running a simulation

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 the following 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.

The full code can be seen here: CODE: First simulation and visualisation, and is also available under examples/getting started code/ within the zip file you download.

Visualise simulation results

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 full code of this page can be seen here: CODE: First simulation and visualisation, and is also available under examples/getting started code/ within the zip file you download.

Note on creating your own models and changing the way visualisation works

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 Advanced functionality

Quick link to Scalability testing & further information