.. _start:

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.

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 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.* 

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 listed code on this page can also be found in the file ``plotAndAnimate.py`` within your /examples folder.


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 :ref:`more`

.. _SimPy: http://simpy.sourceforge.net/
.. _NetworkX: http://networkx.lanl.gov/
    