NDlib - Network Diffusion Library¶
NDlib
is a Python software package that allows to describe, simulate, and study diffusion processes on complex networks.
Date | Python Versions | Main Author | GitHub | pypl |
2021-09-27 | >=3.6 | Giulio Rossetti | Source | Distribution |
If you use NDlib
as support to your research consider citing:
G. Rossetti, L. Milli, S. Rinzivillo, A. Sirbu, D. Pedreschi, F. Giannotti. “NDlib: a Python Library to Model and Analyze Diffusion Processes Over Complex Networks” Journal of Data Science and Analytics. 2017. DOI:0.1007/s41060-017-0086-6 (pre-print available on arXiv)
G. Rossetti, L. Milli, S. Rinzivillo, A. Sirbu, D. Pedreschi, F. Giannotti. “NDlib: Studying Network Diffusion Dynamics” IEEE International Conference on Data Science and Advanced Analytics, DSAA. 2017.
NDlib Dev Team¶
Name | Contribution |
Giulio Rossetti | Library Design/Documentation |
Letizia Milli | Epidemic Models |
Alina Sirbu | Opinion Dynamics Model |
Salvatore Rinzivillo | Visual Platform |
Mathijs Maijer | Continuous Model |
Overview¶
NDlib
is a Python language software package for the describing, simulate, and study diffusion processes on complex networks.
Who uses NDlib?¶
The potential audience for NDlib
includes mathematicians, physicists, biologists, computer scientists, and social scientists.
Goals¶
NDlib
is built upon the NetworkX python library and is intended to provide:
- tools for the study diffusion dynamics on social, biological, and infrastructure networks,
- a standard programming interface and diffusion models implementation that is suitable for many applications,
- a rapid development environment for collaborative, multidisciplinary, projects.
The Python NDlib library¶
NDlib
is a powerful Python package that allows simple and flexible simulations of networks diffusion processes.
Most importantly, NDlib
, as well as the Python programming language, is free, well-supported, and a joy to use.
Free software¶
NDlib
is free software; you can redistribute it and/or modify it under the terms of the BSD License.
We welcome contributions from the community.
EU H2020¶
NDlib
is a result of two European H2020 projects:
- CIMPLEX “Bringing CItizens, Models and Data together in Participatory, Interactive SociaL EXploratories”: under the funding scheme “FETPROACT-1-2014: Global Systems Science (GSS)”, grant agreement #641191.
- SoBigData “Social Mining & Big Data Ecosystem”: under the scheme “INFRAIA-1-2014-2015: Research Infrastructures”, grant agreement #654024.
Download¶
Software¶
Source and binary releases: https://pypi.python.org/pypi/ndlib
Github (latest development): https://github.com/GiulioRossetti/ndlib
Github NDlib-REST: https://github.com/GiulioRossetti/ndlib-rest
Github NDlib-Viz: https://github.com/rinziv/NDLib-Viz
Documentation¶
Installing NDlib¶
Before installing NDlib
, you need to have setuptools installed.
Quick install¶
Get NDlib
from the Python Package Index at pypl.
or install it with
pip install ndlib
and an attempt will be made to find and install an appropriate version that matches your operating system and Python version.
You can install the development version with
pip install git+http://github.com/GiulioRossetti/ndlib.git
Installing from source¶
You can install from source by downloading a source archive file (tar.gz or zip) or by checking out the source files from the GitHub source code repository.
NDlib
is a pure Python package; you don’t need a compiler to build or install it.
Source archive file¶
Download the source (tar.gz or zip file) from pypl or get the latest development version from GitHub
Unpack and change directory to the source directory (it should have the files README.txt and setup.py).
Run python setup.py install to build and install
GitHub¶
Clone the NDlib repostitory (see GitHub for options)
git clone https://github.com/GiulioRossetti/ndlib.git
Change directory to ndlib
Run python setup.py install to build and install
If you don’t have permission to install software on your system, you can install into another directory using the –user, –prefix, or –home flags to setup.py.
For example
python setup.py install --prefix=/home/username/python
or
python setup.py install --home=~
or
python setup.py install --user
If you didn’t install in the standard Python site-packages directory you will need to set your PYTHONPATH variable to the alternate location. See http://docs.python.org/2/install/index.html#search-path for further details.
Requirements¶
Python¶
To use NDlib you need Python 2.7, 3.2 or later.
The easiest way to get Python and most optional packages is to install the Enthought Python distribution “Canopy” or using Anaconda.
There are several other distributions that contain the key packages you need for scientific computing.
Required packages¶
The following are packages required by NDlib
.
NetworkX¶
Provides the graph representation used by the diffusion models implemented in NDlib
.
Download: http://networkx.github.io/download.html
Optional packages¶
The following are optional packages that NDlib
can use to provide additional functions.
Bokeh¶
Provides support to the visualization facilities offered by NDlib
.
Download: http://bokeh.pydata.org/en/latest/
PIL¶
Enables matplotlib animations to be saved to a file, used only by Continuous Model
implementations.
Download: https://pillow.readthedocs.io/en/stable/installation.html
igraph¶
Enables graphs to use layouts from the igraph library, used only by Continuous Model
implementations.
Download: https://igraph.org/python/#downloads
pyintergraph¶
Enables graphs to use layouts from the igraph library, used only by Continuous Model
implementations.
It helps by transforming networkx graphs to igraphs and back
Download: https://gitlab.com/luerhard/pyintergraph#installation
SALib¶
Enables support for sensitivity analysis, used only by Continuous Model Runner
implementations.
Download: https://salib.readthedocs.io/en/latest/getting-started.html#installing-salib
Other packages¶
These are extra packages you may consider using with NDlib
IPython, interactive Python shell, http://ipython.scipy.org/
Tutorial¶
NDlib is built upon networkx and is designed to configure, simulate and visualize diffusion experiments.
Here you can find a few examples to get started with ndlib
: for a more comprehensive tutorial check the official Jupyter Notebook.
Installation¶
In order to install the latest version of the library (with visualization facilities) use
pip install ndlib
Chose a Diffusion model¶
Let’s start importing the required libraries
import networkx as nx
import ndlib.models.epidemics as ep
Once imported the epidemic model module and the networkx library we can initialize the simulation:
# Network Definition
g = nx.erdos_renyi_graph(1000, 0.1)
# Model Selection
model = ep.SIRModel(g)
Configure the simulation¶
Each model has its own parameters: in order to completely instantiate the simulation we need to specify them using a Configuration
object:
import ndlib.models.ModelConfig as mc
# Model Configuration
config = mc.Configuration()
config.add_model_parameter('beta', 0.001)
config.add_model_parameter('gamma', 0.01)
config.add_model_parameter("fraction_infected", 0.05)
model.set_initial_status(config)
The model configuration allows to specify model parameters (as in this scenario) as well as nodes’ and edges’ ones (e.g. individual thresholds).
Moreover it allows to specify the initial fraction of infected nodes using the
fraction_infected
model parameter.
It is also possible to explicitly specify an initial set of infected nodes: see ModelConfig for the complete set of use cases.
Execute the simulation¶
In order to execute the simulation one, or more, iterations must be required using the model.iteration()
and/or model.iteration_bunch(n_iterations)
methods.
# Simulation
iterations = model.iteration_bunch(200)
trends = model.build_trends(iterations)
Visualize the results¶
At the end of the simulation the diffusion trend can be visualized as follows (for matplotlib
change ndlib.viz.bokeh
in ndlib.viz.mpl
)
from bokeh.io import output_notebook, show
from ndlib.viz.bokeh.DiffusionTrend import DiffusionTrend
viz = DiffusionTrend(model, trends)
p = viz.plot(width=400, height=400)
show(p)
Furthermore, a prevalence plot is also made available.
The prevalence plot captures the variation (delta) of nodes for each status in consecutive iterations.
from ndlib.viz.bokeh.DiffusionPrevalence import DiffusionPrevalence
viz2 = DiffusionPrevalence(model, trends)
p2 = viz2.plot(width=400, height=400)
show(p2)
Multiple plots can be combined in a multiplot to provide a complete description of the diffusive process
from ndlib.viz.bokeh.MultiPlot import MultiPlot
vm = MultiPlot()
vm.add_plot(p)
vm.add_plot(p2)
m = vm.plot()
show(m)
Multiplots - implemented only for the bokeh
provider - are also useful to compare different diffusion models applied to the same graph (as well as a same model instantiated with different parameters)
import ndlib.models.epidemics as ep
vm = MultiPlot()
vm.add_plot(p)
# SIS
sis_model = ep.SISModel(g)
config = mc.Configuration()
config.add_model_parameter('beta', 0.001)
config.add_model_parameter('lambda', 0.01)
config.add_model_parameter("fraction_infected", 0.05)
sis_model.set_initial_status(config)
iterations = sis_model.iteration_bunch(200)
trends = sis_model.build_trends(iterations)
viz = DiffusionTrend(sis_model, trends)
p3 = viz.plot(width=400, height=400)
vm.add_plot(p3)
# SI
si_model = ep.SIModel(g)
config = mc.Configuration()
config.add_model_parameter('beta', 0.001)
config.add_model_parameter("fraction_infected", 0.05)
si_model.set_initial_status(config)
iterations = si_model.iteration_bunch(200)
trends = si_model.build_trends(iterations)
viz = DiffusionTrend(si_model, trends)
p4 = viz.plot(width=400, height=400)
vm.add_plot(p4)
# Threshold
th_model = ep.ThresholdModel(g)
config = mc.Configuration()
# Set individual node threshold
threshold = 0.40
for n in g.nodes():
config.add_node_configuration("threshold", n, threshold)
config.add_model_parameter("fraction_infected", 0.30)
th_model.set_initial_status(config)
iterations = th_model.iteration_bunch(60)
trends = th_model.build_trends(iterations)
viz = DiffusionTrend(th_model, trends)
p5 = viz.plot(width=400, height=400)
vm.add_plot(p5)
m = vm.plot()
show(m)
Network Diffusion Library Reference¶
In this section are introduced the components that constitute NDlib
, namely
- The implemented diffusion models (organized in Epidemics and Opinion Dynamics)
- The methodology adopted to configure a general simulation
- The visualization facilities embedded in the library to explore the results
Advanced topics (Custom model definition, Network Diffusion Query language (NDQL), Experiment Server and Visual Framework) are reported in separate sections.
Diffusion Models¶
The analysis of diffusive phenomena that unfold on top of complex networks is a task able to attract growing interests from multiple fields of research.
In order to provide a succinct framing of such complex and extensively studied problem it is possible to split the related literature into two broad, related, sub-classes: Epidemics and Opinion Dynamics.
Moreover, NDlib
also supports the simulation of diffusive processes on top of evolving network topologies: the Dynamic Network Models section the ones NDlib
implements.
Epidemics¶
When we talk about epidemics, we think about contagious diseases caused by biological pathogens, like influenza, measles, chickenpox and sexually transmitted viruses that spread from person to person. However, other phenomena can be linked to the concept of epidemic: think about the spread of computer virus [1] where the agent is a malware that can transmit a copy of itself from computer to computer, or the spread of mobile phone virus [2] [3], or the diffusion of knowledge, innovations, products in an online social network [4] - the so-called “social contagion”, where people are making decision to adopt a new idea or innovation.
Several elements determine the patterns by which epidemics spread through groups of people: the properties carried by the pathogen (its contagiousness, the length of its infectious period and its severity), the structure of the network as well as the mobility patterns of the people involved. Although often treated as similar processes, diffusion of information and epidemic spreading can be easily distinguished by a single feature: the degree of activeness of the subjects they affect.
Indeed, the spreading process of a virus does not require an active participation of the people that catch it (i.e., even though some behaviors acts as contagion facilitators – scarce hygiene, moist and crowded environment – we can assume that no one chooses to get the flu on purpose); conversely, we can argue that the diffusion of an idea, an innovation, or a trend strictly depend not only by the social pressure but also by individual choices.
In NDlib
are implemented the following Epidemic models:
SI¶
The SI model was introduced in 1927 by Kermack [1].
In this model, during the course of an epidemics, a node is allowed to change its status only from Susceptible (S) to Infected (I).
The model is instantiated on a graph having a non-empty set of infected nodes.
SI assumes that if, during a generic iteration, a susceptible node comes into contact with an infected one, it becomes infected with probability β: once a node becomes infected, it stays infected (the only transition allowed is S→I).
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
beta | Model | float in [0, 1] | True | Infection probability |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.epidemics.SIModel.
SIModel
(graph, seed=None)¶ Model Parameters to be specified via ModelConfig
Parameters: beta – The infection rate (float value in [0,1])
-
SIModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
SIModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
SIModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
SIModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
SIModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
SIModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
SIModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of an SI simulation on a random graph: we set the initial set of infected nodes as 5% of the overall population and a probability of infection of 1%.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.SIModel(g)
# Model Configuration
cfg = mc.Configuration()
cfg.add_model_parameter('beta', 0.01)
cfg.add_model_parameter("fraction_infected", 0.05)
model.set_initial_status(cfg)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] |
|
SIS¶
The SIS model was introduced in 1927 by Kermack [1].
In this model, during the course of an epidemics, a node is allowed to change its status from Susceptible (S) to Infected (I).
The model is instantiated on a graph having a non-empty set of infected nodes.
SIS assumes that if, during a generic iteration, a susceptible node comes into contact with an infected one, it becomes infected with probability beta, than it can be switch again to susceptible with probability lambda (the only transition allowed are S→I→S).
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
beta | Model | float in [0, 1] | True | Infection probability | |
lambda | Model | float in [0, 1] | True | Recovery probability |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.epidemics.SISModel.
SISModel
(graph, seed=None)¶ Model Parameters to be specified via ModelConfig
Parameters: - beta – The infection rate (float value in [0,1])
- lambda – The recovery rate (float value in [0,1])
-
SISModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
SISModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
SISModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
SISModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
SISModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
SISModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
SISModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of an SIS simulation on a random graph: we set the initial set of infected nodes as 5% of the overall population, a probability of infection of 1%, and a probability of recovery of 0.5%.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.SISModel(g)
# Model Configuration
cfg = mc.Configuration()
cfg.add_model_parameter('beta', 0.01)
cfg.add_model_parameter('lambda', 0.005)
cfg.add_model_parameter("fraction_infected", 0.05)
model.set_initial_status(cfg)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] |
|
SIR¶
The SIR model was introduced in 1927 by Kermack [1].
In this model, during the course of an epidemics, a node is allowed to change its status from Susceptible (S) to Infected (I), then to Removed (R).
The model is instantiated on a graph having a non-empty set of infected nodes.
SIR assumes that if, during a generic iteration, a susceptible node comes into contact with an infected one, it becomes infected with probability beta, than it can be switch to removed with probability gamma (the only transition allowed are S→I→R).
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Removed | 2 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
beta | Model | float in [0, 1] | True | Infection probability | |
gamma | Model | float in [0, 1] | True | Removal probability |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.epidemics.SIRModel.
SIRModel
(graph, seed=None)¶ Model Parameters to be specified via ModelConfig
Parameters: - beta – The infection rate (float value in [0,1])
- gamma – The recovery rate (float value in [0,1])
-
SIRModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
SIRModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
SIRModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
SIRModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
SIRModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
SIRModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
SIRModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of an SIR simulation on a random graph: we set the initial set of infected nodes as 5% of the overall population, a probability of infection of 1%, and a removal probability of 0.5%.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.SIRModel(g)
# Model Configuration
cfg = mc.Configuration()
cfg.add_model_parameter('beta', 0.01)
cfg.add_model_parameter('gamma', 0.005)
cfg.add_model_parameter("fraction_infected", 0.05)
model.set_initial_status(cfg)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] |
|
SEIR (DT)¶
In the SEIR model [1], during the course of an epidemics, a node is allowed to change its status from Susceptible (S) to Exposed (E) to Infected (I), then to Removed (R).
The model is instantiated on a graph having a non-empty set of infected nodes.
SEIR assumes that if, during a generic iteration, a susceptible node comes into contact with an infected one, it becomes infected after an exposition period with probability beta, than it can switch to removed with probability gamma (the only transition allowed are S→E→I→R).
This implementation assumes discrete time dynamics for the E->I and I->R transitions.
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Exposed | 2 |
Removed | 3 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
beta | Model | float in [0, 1] | True | Infection probability | |
gamma | Model | float in [0, 1] | True | Removal probability | |
alpha | Model | float in [0, 1] | True | Latent period |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.epidemics.SEIRModel.
SEIRModel
(graph, seed=None)¶
-
SEIRModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
SEIRModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
SEIRModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
SEIRModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
SEIRModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
SEIRModel.
iteration
(self)¶ Execute a single model iteration
Parameters: node_status – if the incremental node status has to be returned. Returns: Iteration_id, (optional) Incremental node status (dictionary node->status), Status count (dictionary status->node count), Status delta (dictionary status->node delta)
-
SEIRModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of an SEIR simulation on a random graph: we set the initial set of infected nodes as % of the overall population, a probability of infection of 1%, a removal probability of 0.5% and an incubation period of 5% (e.g. 20 iterations).
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.SEIRModel(g)
# Model Configuration
cfg = mc.Configuration()
cfg.add_model_parameter('beta', 0.01)
cfg.add_model_parameter('gamma', 0.005)
cfg.add_model_parameter('alpha', 0.05)
cfg.add_model_parameter("fraction_infected", 0.05)
model.set_initial_status(cfg)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] | J.L. Aron and I.B. Schwartz. Seasonality and period-doubling bifurcations in an epidemic model. Journal Theoretical Biology, 110:665-679, 1984 |
SEIR (CT)¶
In the SEIR model [1], during the course of an epidemics, a node is allowed to change its status from Susceptible (S) to Exposed (E) to Infected (I), then to Removed (R).
The model is instantiated on a graph having a non-empty set of infected nodes.
SEIR assumes that if, during a generic iteration, a susceptible node comes into contact with an infected one, it becomes infected after an exposition period with probability beta, than it can switch to removed with probability gamma (the only transition allowed are S→E→I→R).
This implementation assumes continuous time dynamics for the E->I and I->R transitions.
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Exposed | 2 |
Removed | 3 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
beta | Model | float in [0, 1] | True | Infection probability | |
gamma | Model | float in [0, 1] | True | Removal probability | |
alpha | Model | float in [0, 1] | True | Latent period |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.epidemics.SEIR_ct_Model.
SEIRctModel
(graph, seed=None)¶
-
SEIRctModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
SEIRctModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
SEIRctModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
SEIRctModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
SEIRctModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
SEIRctModel.
iteration
(self)¶ Execute a single model iteration
Parameters: node_status – if the incremental node status has to be returned. Returns: Iteration_id, (optional) Incremental node status (dictionary node->status), Status count (dictionary status->node count), Status delta (dictionary status->node delta)
-
SEIRctModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of an SEIR simulation on a random graph: we set the initial set of infected nodes as % of the overall population, a probability of infection of 1%, a removal probability of 0.5% and an incubation period of 5% (e.g. 20 iterations).
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.SEIRctModel(g)
# Model Configuration
cfg = mc.Configuration()
cfg.add_model_parameter('beta', 0.01)
cfg.add_model_parameter('gamma', 0.005)
cfg.add_model_parameter('alpha', 0.05)
cfg.add_model_parameter("fraction_infected", 0.05)
model.set_initial_status(cfg)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] | J.L. Aron and I.B. Schwartz. Seasonality and period-doubling bifurcations in an epidemic model. Journal Theoretical Biology, 110:665-679, 1984 |
SEIS (DT)¶
In the SEIS model, during the course of an epidemics, a node is allowed to change its status from Susceptible (S) to Exposed (E) to Infected (I), then again to Susceptible (S).
The model is instantiated on a graph having a non-empty set of infected nodes.
SEIS assumes that if, during a generic iteration, a susceptible node comes into contact with an infected one, it becomes infected after an exposition period with probability beta, than it can switch back to susceptible with probability lambda (the only transition allowed are S→E→I→S).
This implementation assumes discrete time dynamics for the E->I and I->S transitions.
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Exposed | 2 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
beta | Model | float in [0, 1] | True | Infection probability | |
lambda | Model | float in [0, 1] | True | Removal probability | |
alpha | Model | float in [0, 1] | True | Latent period |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.epidemics.SEISModel.
SEISModel
(graph, seed=None)¶ Model Parameters to be specified via ModelConfig
Parameters: - beta – The infection rate (float value in [0,1])
- lambda – The recovery rate (float value in [0,1])
-
SEISModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
SEISModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
SEISModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
SEISModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
SEISModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
SEISModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
SEISModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of an SEIS simulation on a random graph: we set the initial set of infected nodes as 5% of the overall population, a probability of infection of 1%, a removal probability of 0.5% and an latent period of 5% (e.g. 20 iterations).
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.SEISModel(g)
# Model Configuration
cfg = mc.Configuration()
cfg.add_model_parameter('beta', 0.01)
cfg.add_model_parameter('lambda', 0.005)
cfg.add_model_parameter('alpha', 0.05)
cfg.add_model_parameter("fraction_infected", 0.05)
model.set_initial_status(cfg)
# Simulation execution
iterations = model.iteration_bunch(200)
SEIS (CT)¶
In the SEIS model, during the course of an epidemics, a node is allowed to change its status from Susceptible (S) to Exposed (E) to Infected (I), then again to Susceptible (S).
The model is instantiated on a graph having a non-empty set of infected nodes.
SEIS assumes that if, during a generic iteration, a susceptible node comes into contact with an infected one, it becomes infected after an exposition period with probability beta, than it can switch back to susceptible with probability lambda (the only transition allowed are S→E→I→S).
This implementation assumes continuous time dynamics for the E->I and I->S transitions.
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Exposed | 2 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
beta | Model | float in [0, 1] | True | Infection probability | |
lambda | Model | float in [0, 1] | True | Removal probability | |
alpha | Model | float in [0, 1] | True | Latent period |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.epidemics.SEIS_ct_Model.
SEISctModel
(graph, seed=None)¶ Model Parameters to be specified via ModelConfig
Parameters: - beta – The infection rate (float value in [0,1])
- lambda – The recovery rate (float value in [0,1])
-
SEISctModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
SEISctModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
SEISctModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
SEISctModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
SEISctModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
SEISctModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
SEISctModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of an SEIS simulation on a random graph: we set the initial set of infected nodes as 5% of the overall population, a probability of infection of 1%, a removal probability of 0.5% and an latent period of 5% (e.g. 20 iterations).
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.SEISctModel(g)
# Model Configuration
cfg = mc.Configuration()
cfg.add_model_parameter('beta', 0.01)
cfg.add_model_parameter('lambda', 0.005)
cfg.add_model_parameter('alpha', 0.05)
cfg.add_model_parameter("fraction_infected", 0.05)
model.set_initial_status(cfg)
# Simulation execution
iterations = model.iteration_bunch(200)
SWIR¶
The SWIR model was introduced in 2017 by Lee et al. [1].
In this model, during the epidemics, a node is allowed to change its status from Susceptible (S) to Weakened (W) or Infected (I), then to Removed (R).
The model is instantiated on a graph having a non-empty set of infected nodes.
At time t a node in the state I is selected randomly and the states of all neighbors are checked one by one. If the state of a neighbor is S then this state changes either i) to I with probability kappa or ii) to W with probability mu. If the state of a neighbor is W then the state W changes to I with probability nu. We repeat the above process for all nodes in state I and then changes to R for each associated node.
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Weakened | 2 |
Removed | 3 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
kappa | Model | float in [0, 1] | True | ||
mu | Model | float in [0, 1] | True | ||
nu | Model | float in [0, 1] | True |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.epidemics.SWIRModel.
SWIRModel
(graph, seed=None)¶
-
SWIRModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
SWIRModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
SWIRModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
SWIRModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
SWIRModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
SWIRModel.
iteration
(self)¶ Execute a single model iteration
Parameters: node_status – if the incremental node status has to be returned. Returns: Iteration_id, (optional) Incremental node status (dictionary node->status), Status count (dictionary status->node count), Status delta (dictionary status->node delta)
-
SWIRModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of an SEIR simulation on a random graph: we set the initial set of infected nodes as % of the overall population, a probability of infection of 1%, a removal probability of 0.5% and an latent period of 5% (e.g. 20 iterations).
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.SWIRModel(g)
# Model Configuration
cfg = mc.Configuration()
cfg.add_model_parameter('kappa', 0.01)
cfg.add_model_parameter('mu', 0.005)
cfg.add_model_parameter('nu', 0.05)
cfg.add_model_parameter("fraction_infected", 0.05)
model.set_initial_status(cfg)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] |
|
Threshold¶
The Threshold model was introduced in 1978 by Granovetter [1].
In this model during an epidemic, a node has two distinct and mutually exclusive behavioral alternatives, e.g., the decision to do or not do something, to participate or not participate in a riot.
Node’s individual decision depends on the percentage of its neighbors that have made the same choice, thus imposing a threshold.
The model works as follows: - each node has its own threshold; - during a generic iteration every node is observed: if the percentage of its infected neighbors is greater than its threshold it becomes infected as well.
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
threshold | Node | float in [0, 1] | 0.1 | False | Individual threshold |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.epidemics.ThresholdModel.
ThresholdModel
(graph, seed=None)¶ - Node Parameters to be specified via ModelConfig
Parameters: threshold – The node threshold. If not specified otherwise a value of 0.1 is assumed for all nodes.
-
ThresholdModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
ThresholdModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
ThresholdModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
ThresholdModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
ThresholdModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
ThresholdModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
ThresholdModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of a Threshold model simulation on a random graph: we set the initial set of infected nodes as 1% of the overall population, and assign a threshold of 0.25 to all the nodes.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.ThresholdModel(g)
# Model Configuration
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)
# Setting node parameters
threshold = 0.25
for i in g.nodes():
config.add_node_configuration("threshold", i, threshold)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] |
|
Generalised Threshold¶
The Generalised Threshold model was introduced in 2017 by Török and Kertesz [1].
In this model, during an epidemics, a node is allowed to change its status from Susceptible to Infected.
The model is instantiated on a graph having a non-empty set of infected nodes.
The model is defined as follows:
- At time t nodes become Infected with rate mu t/tau
- Nodes for which the ratio of the active friends dropped below the threshold are moved to the Infected queue
- Nodes in the Infected queue become infected with rate tau. If this happens check all its friends for threshold
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
threshold | Node | float in [0, 1] | 0.1 | False | Individual threshold |
tau | Model | int | True | Adoption threshold rate | |
mu | Model | int | True | Exogenous timescale |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.epidemics.GeneralisedThresholdModel.
GeneralisedThresholdModel
(graph, seed=None)¶ - Node Parameters to be specified via ModelConfig
Parameters: threshold – The node threshold. If not specified otherwise a value of 0.1 is assumed for all nodes.
-
GeneralisedThresholdModel.
__init__
(graph)¶ Model Constructor :param graph: A networkx graph object
-
GeneralisedThresholdModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
GeneralisedThresholdModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
GeneralisedThresholdModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
GeneralisedThresholdModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
GeneralisedThresholdModel.
iteration
(self)¶ Execute a single model iteration :return: Iteration_id, Incremental node status (dictionary node->status)
-
GeneralisedThresholdModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of a Threshold model simulation on a random graph: we set the initial set of infected nodes as 1% of the overall population, and assign a threshold of 0.25 to all the nodes.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.GeneralisedThresholdModel(g)
# Model Configuration
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)
config.add_model_parameter('tau', 5)
config.add_model_parameter('mu', 5)
# Setting node parameters
threshold = 0.25
for i in g.nodes():
config.add_node_configuration("threshold", i, threshold)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] | János Török and János Kertész “Cascading collapse of online social networks” Scientific reports, vol. 7 no. 1, 2017 |
Kertesz Threshold¶
The Kertesz Threshold model was introduced in 2015 by Ruan et al. [1] and it is an extension of the Watts threshold model [2].
The authors extend the classical model introducing a density r of blocked nodes – nodes which are immune to social influence – and a probability of spontaneous adoption p to capture external influence.
Thus, the model distinguishes three kinds of node: Blocked (B), Susceptible (S) and Adoptiong (A). The latter class breaks into two categories: vulnerable and stable nodes. A node can adopt either under its neighbors’ influence, or spontaneously, due to endogenous effects.
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Blocked | -1 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
adopter_rate | Model | float in [0, 1] | 0 | False | Exogenous adoption rate |
percentage_blocked | Model | float in [0, 1] | 0.1 | False | Blocked nodes |
threshold | Node | float in [0, 1] | 0.1 | False | Individual threshold |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The initial blocked nodes can be defined via:
- percentage_blocked: Model Parameter, float in [0, 1]
- Blocked: Status Parameter, set of nodes
In both cases, the two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.epidemics.KerteszThresholdModel.
KerteszThresholdModel
(graph, seed=None)¶ - Node/Model Parameters to be specified via ModelConfig
Parameters: - threshold – The node threshold. As default a value of 0.1 is assumed for all nodes.
- adopter_rate – The probability of spontaneous adoptions. Defaults value 0.
- fraction_infected – The percentage of blocked nodes. Default value 0.1.
-
KerteszThresholdModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
KerteszThresholdModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
KerteszThresholdModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
KerteszThresholdModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
KerteszThresholdModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
KerteszThresholdModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
KerteszThresholdModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of a Kertesz Threshold model simulation on a random graph: we set the initial infected as well blocked node sets equals to the 10% of the overall population, assign a threshold of 0.25 to all the nodes and impose an probability of spontaneous adoptions of 40%.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.KerteszThresholdModel(g)
# Model Configuration
config = mc.Configuration()
config.add_model_parameter('adopter_rate', 0.4)
config.add_model_parameter('percentage_blocked', 0.1)
config.add_model_parameter('fraction_infected', 0.1)
# Setting node parameters
threshold = 0.25
for i in g.nodes():
config.add_node_configuration("threshold", i, threshold)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] |
|
[2] |
|
Independent Cascades¶
The Independent Cascades model was introduced by Kempe et all in 2003 [1].
This model starts with an initial set of active nodes A0: the diffusive process unfolds in discrete steps according to the following randomized rule:
- When node v becomes active in step t, it is given a single chance to activate each currently inactive neighbor w; it succeeds with a probability p(v,w).
- If w has multiple newly activated neighbors, their attempts are sequenced in an arbitrary order.
- If v succeeds, then w will become active in step t + 1; but whether or not v succeeds, it cannot make any further attempts to activate w in subsequent rounds.
- The process runs until no more activations are possible.
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Removed | 2 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
Edge threshold | Edge | float in [0, 1] | 0.1 | False | Edge threshold |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.epidemics.IndependentCascadesModel.
IndependentCascadesModel
(graph, seed=None)¶ Edge Parameters to be specified via ModelConfig
Parameters: threshold – The edge threshold. As default a value of 0.1 is assumed for all edges.
-
IndependentCascadesModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
IndependentCascadesModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
IndependentCascadesModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
IndependentCascadesModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
IndependentCascadesModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
IndependentCascadesModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
IndependentCascadesModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of an Independent Cascades model simulation on a random graph: we set the initial set of infected nodes as 1% of the overall population, and assign a threshold of 0.1 to all the edges.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.IndependentCascadesModel(g)
# Model Configuration
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)
# Setting the edge parameters
threshold = 0.1
for e in g.edges():
config.add_edge_configuration("threshold", e, threshold)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] |
|
Profile¶
The Profile model was introduced in 2017 by Milli et al. [1].
The Profile model assumes that the diffusion process is only apparent; each node decides to adopt or not a given behavior – once known its existence – only on the basis of its own interests.
In this scenario the peer pressure is completely ruled out from the overall model: it is not important how many of its neighbors have adopted a specific behaviour, if the node does not like it, it will not change its interests.
Each node has its own profile describing how likely it is to accept a behaviour similar to the one that is currently spreading.
The diffusion process starts from a set of nodes that have already adopted a given behaviour S:
- for each of the susceptible nodes’ in the neighborhood of a node u in S an unbalanced coin is flipped, the unbalance given by the personal profile of the susceptible node;
- if a positive result is obtained the susceptible node will adopt the behaviour, thus becoming infected.
- if the blocked status is enabled, after having rejected the adoption with probability
blocked
a node becomes immune to the infection. - every iteration
adopter_rate
percentage of nodes spontaneous became infected to endogenous effects.
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Blocked | -1 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
profile | Node | float in [0, 1] | 0.1 | False | Node profile |
blocked | Model | float in [0, 1] | 0 | False | Blocked nodes |
adopter_rate | Model | float in [0, 1] | 0 | False | Autonomous adoption |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.epidemics.ProfileModel.
ProfileModel
(graph, seed=None)¶ - Node Parameters to be specified via ModelConfig
Parameters: profile – The node profile. As default a value of 0.1 is assumed for all nodes.
-
ProfileModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
ProfileModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
ProfileModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
ProfileModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
ProfileModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
ProfileModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
ProfileModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of a Profile model simulation on a random graph: we set the initial infected node set to the 10% of the overall population and assign a profile of 0.15 to all the nodes.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.ProfileModel(g)
config = mc.Configuration()
config.add_model_parameter('blocked', 0)
config.add_model_parameter('adopter_rate', 0)
config.add_model_parameter('fraction_infected', 0.1)
# Setting nodes parameters
profile = 0.15
for i in g.nodes():
config.add_node_configuration("profile", i, profile)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] | Letizia Milli, Giulio Rossetti, Dino Pedreschi, Fosca Giannotti, “Information Diffusion in Complex Networks: The Active/Passive Conundrum,” Proceedings of International Conference on Complex Networks and their Applications, (pp. 305-313). Springer, Cham. 2017 |
Profile Threshold¶
The Profile-Threshold model was introduced in 2017 by Milli et al. [1].
The Profile-Threshold model assumes the existence of node profiles that act as preferential schemas for individual tastes but relax the constraints imposed by the Profile model by letting nodes influenceable via peer pressure mechanisms.
The peer pressure is modeled with a threshold.
The diffusion process starts from a set of nodes that have already adopted a given behaviour S:
- for each of the susceptible node an unbalanced coin is flipped if the percentage of its neighbors that are already infected excedes its threhosld. As in the Profile Model the coin unbalance is given by the personal profile of the susceptible node;
- if a positive result is obtained the susceptible node will adopt the behaviour, thus becoming infected.
- if the blocked status is enabled, after having rejected the adoption with probability
blocked
a node becomes immune to the infection. - every iteration
adopter_rate
percentage of nodes spontaneous became infected to endogenous effects.
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Blocked | -1 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
threshold | Node | float in [0, 1] | 0.1 | False | Individual threshold |
profile | Node | float in [0, 1] | 0.1 | False | Node profile |
blocked | Model | float in [0, 1] | 0 | False | Blocked nodes |
adopter_rate | Model | float in [0, 1] | 0 | False | Autonomous adoption |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.epidemics.ProfileThresholdModel.
ProfileThresholdModel
(graph, seed=None)¶ Node Parameters to be specified via ModelConfig
Parameters: - profile – The node profile. As default a value of 0.1 is assumed for all nodes.
- threshold – The node threshold. As default a value of 0.1 is assumed for all nodes.
-
ProfileThresholdModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
ProfileThresholdModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
ProfileThresholdModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
ProfileThresholdModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
ProfileThresholdModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
ProfileThresholdModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
ProfileThresholdModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of a Profile Threshold model simulation on a random graph: we set the initial infected node set to the 10% of the overall population, assign a profile of 0.25 and a threshold of 0.15 to all the nodes.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.ProfileThresholdModel(g)
config = mc.Configuration()
config.add_model_parameter('blocked', 0)
config.add_model_parameter('adopter_rate', 0)
config.add_model_parameter('fraction_infected', 0.1)
# Setting nodes parameters
threshold = 0.15
profile = 0.25
for i in g.nodes():
config.add_node_configuration("threshold", i, threshold)
config.add_node_configuration("profile", i, profile)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] | Letizia Milli, Giulio Rossetti, Dino Pedreschi, Fosca Giannotti, “Information Diffusion in Complex Networks: The Active/Passive Conundrum,” Proceedings of International Conference on Complex Networks and their Applications, (pp. 305-313). Springer, Cham. 2017 |
UTLDR¶
The UTLDR model [1] describe a complex framework allowing to extend SEIR/SEIS to incorporate medical and non medical interventions. The acronym summarizes the five macro statuses an agent can be involved into:
- U ndetected
- T tested
- L ocked
- D ead
- R ecovered
The U macro status follows the same rules of a classic SEIR(S) model and model the general epidemic evolution when no intervention is established.
The T macro status implements Testing (e.g., identification of exposed/infected agents) and model different classes of response (e.g., quarantine, hospitalization, ICU ospitalization).
The L macro status implements Lockdowns (that can be fine tuned on node attributes) and, as such, social contacts reduction.
Finally, the R and D statuses model the final outcome of an infection (either death or recovery) and are sensible to the various paths for reaching them.
Moreover, UTLDR allows also to include (effective/uneffective) vaccination effects.
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Exposed | 2 |
Recovered | 3 |
Identified Exposed | 4 |
Hospitalized Mild conditions | 5 |
Hospitalized in ICU | 6 |
Hospitalized in Severe conditions (not ICU) | 7 |
Lockdown Susceptible | 8 |
Lockdown Exposed | 9 |
Lockdown Infected | 10 |
Dead | 11 |
Vaccinated | 12 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
sigma | Model | float in [0, 1] | True | Incubation rate | |
beta | Model | float in [0, 1] | True | Infection rate | |
gamma | Model | float in [0, 1] | True | Recovery rate (Mild, Asymtomatic, Paucisymtomatic) | |
gamma_t | Model | float in [0, 1] | 0.6 | False | Recovery rate (Severe in ICU) |
gamma_f | Model | float in [0, 1] | 0.95 | False | Recovery rate (Severe not ICU) |
omega | Model | float in [0, 1] | 0 | False | Death probability (Mild, Asymtomatic, Paucisymtomatic) |
omega_t | Model | float in [0, 1] | 0 | False | Death probability (Severe in ICU) |
omega_f | Model | float in [0, 1] | 0 | False | Death probability (Severe not ICU) |
phi_e | Model | float in [0, 1] | 0 | False | Testing probability (if Exposed) |
phi_i | Model | float in [0, 1] | 0 | False | Testing probability (if Infected) |
kappa_e | Model | float in [0, 1] | 0.7 | False | 1 - False Negative probability (if Exposed) |
kappa_i | Model | float in [0, 1] | 0.9 | False | 1 - False Negative probability (if Infected) |
epsilon_e | Model | float in [0, 1] | 1 | False | Social restriction, percentage of pruned edges (Quarantine) |
epsilon_l | Model | float in [0, 1] | 1 | False | Social restriction, percentage of pruned edges (Lockdown) |
lambda | Model | float in [0, 1] | 1 | False | Lockdown effectiveness |
mu | Model | float in [0, 1] | 1 | False | Lockdown duration (1/expected iterations) |
p | Model | float in [0, 1] | 0 | False | Probability of long range (random) interactions |
p_l | Model | float in [0, 1] | 0 | False | Probability of long range (random) interactions (Lockdown) |
lsize | Model | float in [0, 1] | 0.25 | False | Percentage of long range interactions w.r.t. short range ones |
icu_b | Model | int in [0, inf] | N | False | ICU beds availability |
iota | Model | float in [0, 1] | 1 | False | Severe case probability (requiring ICU) |
z | Model | float in [0, 1] | 0 | False | Probability of infection from corpses |
s | Model | float in [0, 1] | 0 | False | Probability of no immunization after recovery |
v | Model | float in [0, 1] | 0 | False | Vaccination probability (once per agent) |
f | Model | float in [0, 1] | 0 | False | Probability of vaccination nullification (1/temporal coverage) |
activity | Node | float in [0, 1] | 1 | False | Node’s interactions per iteration (percentage of neighbors) |
segment | Node | str | None | False | Node’s class (e.g., age, gender) |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.epidemics.UTLDRModel.
UTLDRModel
(graph, seed=None)¶
-
UTLDRModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
UTLDRModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
UTLDRModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
UTLDRModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
UTLDRModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
UTLDRModel.
iteration
(self)¶ Parameters: node_status – Returns:
-
UTLDRModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
-
UTLDRModel.
set_lockdown
(self)¶ Impose the beginning of a lockdown
Parameters: households – (optional) dictionary specifying the households for each node <node_id -> list(nodes in household)> Returns:
-
UTLDRModel.
unset_lockdown
(self)¶ Remove the lockdown social limitations
Returns:
-
UTLDRModel.
add_ICU_beds
(self, n)¶ Add/Subtract beds in intensive care
Parameters: n – number of beds to add/remove Returns:
In the code below is shown an example of instantiation and execution of an UTLDR simulation on a random graph.
import networkx as nx
import numpy as np
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as epd
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
model = epd.UTLDRModel(g)
config = mc.Configuration()
# Undetected
config.add_model_parameter("sigma", 0.05)
config.add_model_parameter("beta", {"M": 0.25, "F": 0})
config.add_model_parameter("gamma", 0.05)
config.add_model_parameter("omega", 0.01)
config.add_model_parameter("p", 0.04)
config.add_model_parameter("lsize", 0.2)
# Testing
config.add_model_parameter("phi_e", 0.03)
config.add_model_parameter("phi_i", 0.1)
config.add_model_parameter("kappa_e", 0.03)
config.add_model_parameter("kappa_i", 0.1)
config.add_model_parameter("gamma_t", 0.08)
config.add_model_parameter("gamma_f", 0.1)
config.add_model_parameter("omega_t", 0.01)
config.add_model_parameter("omega_f", 0.08)
config.add_model_parameter("epsilon_e", 1)
config.add_model_parameter("icu_b", 10)
config.add_model_parameter("iota", 0.20)
config.add_model_parameter("z", 0.2)
config.add_model_parameter("s", 0.05)
# Lockdown
config.add_model_parameter("lambda", 0.8)
config.add_model_parameter("epsilon_l", 5)
config.add_model_parameter("mu", 0.05)
config.add_model_parameter("p_l", 0.04)
# Vaccination
config.add_model_parameter("v", 0.15)
config.add_model_parameter("f", 0.02)
nodes = g.nodes
ngender = ['M', 'F']
work = ['school', 'PA', 'hospital', 'none']
for i in nodes:
config.add_node_configuration("activity", i, 1)
config.add_node_configuration("work", i, np.random.choice(work, 2))
config.add_node_configuration("segment", i, np.random.choice(ngender, 1)[0])
model.set_initial_status(config)
iterations = model.iteration_bunch(10)
households = {0: [1, 2, 3, 4], 5: [6, 7]}
model.set_lockdown(households, ['PA', 'school'])
iterations = model.iteration_bunch(10)
model.unset_lockdown(['PA'])
iterations = model.iteration_bunch(10)
model.set_lockdown(households)
iterations = model.iteration_bunch(10)
model.unset_lockdown(['school'])
iterations = model.iteration_bunch(10)
model.add_ICU_beds(5)
iterations = model.iteration_bunch(10)
[1] |
|
Independent Cascades with Community Embeddedness and Permeability¶
The Independent Cascades with Community Embeddedness and Permeability model was introduced by Milli and Rossetti in 2020 [1].
This model is a combination of ICE and ICP methods.
The ICEP model starts with an initial set of active nodes A0; the diffusive process unfolds in discrete steps according to the following randomized rule:
- When node v becomes active in step t, it is given a single chance to activate each currently inactive neighbor u. If v and u belong to the same community, the method acts as the ICE model, otherwise as the ICP model.
- If u has multiple newly activated neighbors, their attempts are sequenced in an arbitrary order.
- If v succeeds, then u will become active in step t + 1; but whether or not v succeeds, it cannot make any further attempts to activate u in subsequent rounds.
- The process runs until no more activations are possible.
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Removed | 2 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
Edge threshold | Edge | float in [0, 1] | 0.1 | False | Edge threshold |
Community permeability | Model | float in [0, 1] | 0.5 | True | Community Permeability |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.epidemics.ICEPModel.
ICEPModel
(graph)¶ Edge Parameters to be specified via ModelConfig
Parameters: permeability – The degree of permeability of a community toward outgoing diffusion processes
-
ICEPModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
ICEPModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
ICEPModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
ICEPModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
ICEPModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
ICEPModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
ICEPModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of an ICEP model simulation on a random graph: we set the initial set of infected nodes as 1% of the overall population, assign a threshold of 0.1 to all the edges and set the community permeability equal 0.3.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.ICEPModel(g)
# Model Configuration
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)
config.add_model_parameter('permeability', 0.3)
# Setting the edge parameters
threshold = 0.1
for e in g.edges():
config.add_edge_configuration("threshold", e, threshold)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] |
|
Independent Cascades with Community Permeability¶
The Independent Cascades with Community Permeability model was introduced by Milli and Rossetti in 2019 [1].
This model is a variation of the well-known Independent Cascade (IC), and it is designed to embed community awareness into the IC model. This model exploits the idea of permeability. A community is “permeable” to a given content if it permits that content to spread from it fast (or vice-versa, if it permits the content to be easily received from nodes outside the community). Conversely, a community has a low degree of permeability if it dampens the diffusion probability across its border.
The ICP model starts with an initial set of active nodes A0; the diffusive process unfolds in discrete steps according to the following randomized rule:
- When node v becomes active in step t, it is given a single chance to activate each currently inactive neighbor u. If v and u belong to the same community, the method works as a standard IC model (it succeeds with a probability p(v,u)); instead, if the nodes are part of to different communities, the likelihood p(v,u) is dampened of a factor \(\eta\) (the community permeability parameter).
- If u has multiple newly activated neighbors, their attempts are sequenced in an arbitrary order.
- If v succeeds, then u will become active in step t + 1; but whether or not v succeeds, it cannot make any further attempts to activate u in subsequent rounds.
- The process runs until no more activations are possible.
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Removed | 2 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
Edge threshold | Edge | float in [0, 1] | 0.1 | False | Edge threshold |
Community permeability | Model | float in [0, 1] | 0.5 | True | Community Permeability |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
ICPModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
ICPModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
ICPModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
ICPModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
ICPModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
ICPModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
ICPModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of an ICP model simulation on a random graph: we set the initial set of infected nodes as 1% of the overall population, assign a threshold of 0.1 to all the edges and set the community permeability equal 0.3.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.ICPModel(g)
# Model Configuration
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)
config.add_model_parameter('permeability', 0.3)
# Setting the edge parameters
threshold = 0.1
for e in g.edges():
config.add_edge_configuration("threshold", e, threshold)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] |
|
Independent Cascades with Community Embeddedness¶
The Independent Cascades with Community Embeddedness model was introduced by Milli and Rossetti in 2019 [1].
This model is a variation of the well-known Independent Cascade (IC), and it is designed to embed community awareness into the IC model. The probability p(u,v) of the IC model is replaced by the edge embeddedness.
The embeddedness of an edge \((u,v)\) with \(u,v \in C\) is defined as: \(e_{u,v} = \frac{\phi_{u,v}}{|\Gamma(u) \cup \Gamma(v)|}\) where \(\phi_{u,v}\) is the number of common neighbors of u and v within \(C\), and \(\Gamma(u)\) ( \(\Gamma(v)\)) is the set of neighbors of the node u (v) in the analyzed graph G.
The ICE model starts with an initial set of active nodes A0; the diffusive process unfolds in discrete steps according to the following randomized rule:
- When node v becomes active in step t, it is given a single chance to activate each currently inactive neighbor u. If v and u belong to the same community, it succeeds with a probability \(e_{u,v}\); otherwise with probability \(\min\{e_{z,v}|(z, v)\in E\}\).
- If u has multiple newly activated neighbors, their attempts are sequenced in an arbitrary order.
- If v succeeds, then u will become active in step t + 1; but whether or not v succeeds, it cannot make any further attempts to activate u in subsequent rounds.
- The process runs until no more activations are possible.
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Removed | 2 |
The model is parameter free
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.epidemics.ICEModel.
ICEModel
(graph)¶ Parameter free model: probability of diffusion tied to community embeddedness of individual nodes
-
ICEModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
ICEModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
ICEModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
ICEModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
ICEModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
ICEModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
ICEModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of an ICE model simulation on a random graph: we set the initial set of infected nodes as 1% of the overall population.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.ICEModel(g)
# Model Configuration
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] |
|
Opinion Dynamics¶
A different field related with modelling social behaviour is that of opinion dynamics.
Recent years have witnessed the introduction of a wide range of models that attempt to explain how opinions form in a population [5], taking into account various social theories (e.g. bounded confidence [6] or social impact [7]).
These models have a lot in common with those seen in epidemics and spreading. In general, individuals are modelled as agents with a state and connected by a social network.
The social links can be represented by a complete graph (mean field models) or by more realistic complex networks, similar to epidemics and spreading.
The state is typically represented by variables, that can be discrete (similar to the case of spreading), but also continuous, representing for instance a probability to choose one option or another [8] . The state of individuals changes in time, based on a set of update rules, mainly through interaction with the neighbours.
While in many spreading and epidemics models this change is irreversible (susceptible to infected), in opinion dynamics the state can oscillate freely between the possible values, simulating thus how opinions change in reality.
A different important aspect in opinion dynamics is external information, which can be interpreted as the effect of mass media. In general external information is represented as a static individual with whom all others can interact, again present also in spreading models. Hence, it is clear that the two model categories have enough in common to be implemented under a common framework, which is why we introduced both in our framework.
In NDlib
are implemented the following Opinion Dynamics models:
Voter¶
The Voter model is one of the simplest models of opinion dynamics, originally introduced to analyse competition of species [1] and soon after applied to model elections [2].
The model assumes the opinion of an individual to be a discrete variable ±1.
The state of the population varies based on a very simple update rule: at each iteration, a random individual is selected, who then copies the opinion of one random neighbour.
Starting from any initial configuration, on a complete network the entire population converges to consensus on one of the two options. The probability that consensus is reached on opinion +1 is equal to the initial fraction of individuals holding that opinion [3].
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The initial blocked nodes can be defined via:
- percentage_blocked: Model Parameter, float in [0, 1]
- Blocked: Status Parameter, set of nodes
In both cases, the two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.opinions.VoterModel.
VoterModel
(graph, seed=None)¶
-
VoterModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
VoterModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
VoterModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
VoterModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
VoterModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
VoterModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
VoterModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of a Voter model simulation on a random graph: we set the initial infected node set to the 10% of the overall population.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.opinions as op
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = op.VoterModel(g)
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] |
|
[2] |
|
[3] | P.L.Krapivsky,S.Redner,andE.Ben-Naim,Akineticviewofstatistical physics. Cambridge University Press, 2010. |
Q-Voter¶
The Q-Voter model was introduced as a generalisation of discrete opinion dynamics models [1].
Here, N individuals hold an opinion ±1. At each time step, a set of q neighbours are chosen and, if they agree, they influence one neighbour chosen at random, i.e. this agent copies the opinion of the group. If the group does not agree, the agent flips its opinion with probability ε.
It is clear that the voter and Sznajd models are special cases of this more recent model (q = 1,ε = 0 and q = 2,ε = 0).
Analytic results for q ≤ 3 validate the numerical results obtained for the special case models, with transitions from a ordered phase (small ε) to a disordered one (large ε). For q > 3, a new type of transition between the two phases appears, which consist of passing through an intermediate regime where the final state depends on the initial condition. We implemented in NDlib the model with ε = 0.
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
q | Model | int in [0, V(G)] | True | Number of neighbours |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.opinions.QVoterModel.
QVoterModel
(graph, seed=None)¶ Node Parameters to be specified via ModelConfig
Parameters: q – the number of neighbors that affect the opinion of a node
-
QVoterModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
QVoterModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
QVoterModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
QVoterModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
QVoterModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
QVoterModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
QVoterModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of a Q-Voter model simulation on a random graph: we set the initial infected node set to the 10% of the overall population and the number q of influencing neighbors equals to 5.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.opinions as op
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = op.QVoterModel(g)
config = mc.Configuration()
config.add_model_parameter("q", 5)
config.add_model_parameter('fraction_infected', 0.1)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] |
|
Majority Rule¶
The Majority Rule model is a discrete model of opinion dynamics, proposed to describe public debates [1].
Agents take discrete opinions ±1, just like the Voter model. At each time step a group of r agents is selected randomly and they all take the majority opinion within the group.
The group size can be fixed or taken at each time step from a specific distribution. If r is odd, then the majority opinion is always defined, however if r is even there could be tied situations. To select a prevailing opinion in this case, a bias in favour of one opinion (+1) is introduced.
This idea is inspired by the concept of social inertia [2].
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
q | Model | int in [0, V(G)] | True | Number of neighbours |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.opinions.MajorityRuleModel.
MajorityRuleModel
(graph, seed=None)¶
-
MajorityRuleModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
MajorityRuleModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
MajorityRuleModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
MajorityRuleModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
MajorityRuleModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
MajorityRuleModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
MajorityRuleModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of a Majority Rule model simulation on a random graph: we set the initial infected node set to the 10% of the overall population.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.opinions as op
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = op.MajorityRuleModel(g)
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] | S.Galam, “Minority opinion spreading in random geometry.” Eur.Phys. J. B, vol. 25, no. 4, pp. 403–406, 2002. |
[2] | R.Friedman and M.Friedman, “The Tyranny of the Status Quo.” Orlando, FL, USA: Harcourt Brace Company, 1984. |
Sznajd¶
The Sznajd model [1] is a variant of spin model employing the theory of social impact, which takes into account the fact that a group of individuals with the same opinion can influence their neighbours more than one single individual.
In the original model the social network is a 2-dimensional lattice, however we also implemented the variant on any complex networks.
Each agent has an opinion σi = ±1. At each time step, a pair of neighbouring agents is selected and, if their opinion coincides, all their neighbours take that opinion.
The model has been shown to converge to one of the two agreeing stationary states, depending on the initial density of up-spins (transition at 50% density).
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.opinions.SznajdModel.
SznajdModel
(graph, seed=None)¶
-
SznajdModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
SznajdModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
SznajdModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
SznajdModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
SznajdModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
SznajdModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
SznajdModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of a Sznajd model simulation on a random graph: we set the initial infected node set to the 10% of the overall population.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.opinions as op
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = op.SznajdModel(g)
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] |
|
Cognitive Opinion Dynamics¶
The Cognitive Opinion Dynamics model was introduced in [1], which models the state of individuals taking into account several cognitively-grounded variables.
The aim of the model is to simulate response to risk in catastrophic events in the presence of external (institutional) information.
The individual opinion is modelled as a continuous variable Oi ∈ [0, 1], representing the degree of perception of the risk (how probable it is that the catastrophic event will actually happen).
This opinion evolves through interactions with neighbours and external information, based on four internal variables for each individual i:
- risk sensitivity (Ri ∈ {−1, 0, 1}),
- tendency to inform others (βi ∈ [0,1]),
- trust in institutions (Ti ∈ [0,1]), and
- trust in peers (Πi = 1 − Ti).
These values are generated when the population is initialised and stay fixed during the simulation.
The update rules define how Oi values change in time.
The model was shown to be able to reproduce well various real situations. In particular, it is visible that risk sensitivity is more important than trust in institutional information when it comes to evaluating risky situations.
Node statuses are continuous values in [0,1].
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
I | Model | float in [0, 1] | True | External information | |
T_range_min | Model | float in [0, 1] | True | Minimum of the range of initial values for T | |
T_range_max | Model | float in [0, 1] | True | Maximum of the range of initial values for T | |
B_range_min | Model | float in [0, 1] | True | Minimum of the range of initial values for B | |
B_range_max | Model | float in [0, 1] | True | Maximum of the range of initial values for B | |
R_fraction_negative | Model | float in [0, 1] | True | Fraction of nodes having R=-1 | |
R_fraction_neutral | Model | float in [0, 1] | True | Fraction of nodes having R=0 | |
R_fraction_positive | Model | float in [0, 1] | True | Fraction of nodes having R=1 |
The following relation should hold: R_fraction_negative+R_fraction_neutral+R_fraction_positive=1
.
To achieve this, the fractions selected will be normalised to sum 1.
The initial state is generated randomly uniformly from the domain defined by model parameters.
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.opinions.CognitiveOpDynModel.
CognitiveOpDynModel
(graph, seed=None)¶ Model Parameters to be specified via ModelConfig
Parameters: - I – external information value in [0,1]
- T_range_min – the minimum of the range of initial values for T. Range [0,1].
- T_range_max – the maximum of the range of initial values for T. Range [0,1].
- B_range_min – the minimum of the range of initial values for B. Range [0,1]
- B_range_max – the maximum of the range of initial values for B. Range [0,1].
- R_fraction_negative – fraction of individuals having the node parameter R=-1.
- R_fraction_positive – fraction of individuals having the node parameter R=1
- R_fraction_neutral – fraction of individuals having the node parameter R=0
The following relation should hold: R_fraction_negative+R_fraction_neutral+R_fraction_positive=1. To achieve this, the fractions selected will be normalised to sum 1. Node states are continuous values in [0,1].
The initial state is generated randomly uniformly from the domain defined by model parameters.
-
CognitiveOpDynModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
CognitiveOpDynModel.
set_initial_status
(self, configuration)¶ Override behaviour of methods in class DiffusionModel. Overwrites initial status using random real values. Generates random node profiles.
-
CognitiveOpDynModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
CognitiveOpDynModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
CognitiveOpDynModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
CognitiveOpDynModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
CognitiveOpDynModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of a Cognitive Opinion Dynamics model simulation on a random graph: we set the initial infected node set to the 10% of the overall population, the external information value to 015, the B and T intervals equal to [0,1] and the fraction of positive/neutral/infected equal to 1/3.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.opinions as op
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = op.CognitiveOpDynModel(g)
# Model Configuration
config = mc.Configuration()
config.add_model_parameter("I", 0.15)
config.add_model_parameter("B_range_min", 0)
config.add_model_parameter("B_range_max", 1)
config.add_model_parameter("T_range_min", 0)
config.add_model_parameter("T_range_max", 1)
config.add_model_parameter("R_fraction_negative", 1.0 / 3)
config.add_model_parameter("R_fraction_neutral", 1.0 / 3)
config.add_model_parameter("R_fraction_positive", 1.0 / 3)
config.add_model_parameter('fraction_infected', 0.1)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(200)
[1] |
|
Algorithmic Bias¶
Note
The Algorithmic Bias model will be officially released in NDlib 4.0.1
The Algorithmic Bias model considers a population of individuals, where each individual holds a continuous opinion in the interval [0,1]. Individuals are connected by a social network, and interact pairwise at discrete time steps. The interacting pair is selected from the population at each time point in such a way that individuals that have close opinion values are selected more often, to simulate algorithmic bias. The parameter gamma controls how large this effect is. Specifically, the first individual in the interacting pair is selected randomly, while the second individual is selected based on a probability that decreases with the distance from the opinion of the first individual, i.e. directly proportional with the distance raised to the power -gamma.
After interaction, the two opinions may change, depending on a so called bounded confidence parameter, epsilon. This can be seen as a measure of the open-mindedness of individuals in a population. It defines a threshold on the distance between the opinion of the two individuals, beyond which communication between individuals is not possible due to conflicting views. Thus, if the distance between the opinions of the selected individuals is lower than epsilon, the two individuals adopt their average opinion. Otherwise nothing happens.
Note: setting gamma=0 reproduce the results for the Deffuant model.
Node statuses are continuous values in [0,1].
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
epsilon | Model | float in [0, 1] | True | Bounded confidence threshold | |
gamma | Model | int in [0, 100] | True | Algorithmic bias |
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.opinions.AlgorithmicBiasModel.
AlgorithmicBiasModel
(graph, seed=None)¶ Model Parameters to be specified via ModelConfig
Parameters: - epsilon – bounded confidence threshold from the Deffuant model, in [0,1]
- gamma – strength of the algorithmic bias, positive, real
Node states are continuous values in [0,1].
The initial state is generated randomly uniformly from the domain [0,1].
-
AlgorithmicBiasModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
AlgorithmicBiasModel.
set_initial_status
(self, configuration)¶ Override behaviour of methods in class DiffusionModel. Overwrites initial status using random real values.
-
AlgorithmicBiasModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
AlgorithmicBiasModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
AlgorithmicBiasModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
AlgorithmicBiasModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
AlgorithmicBiasModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of a AlgorithmicBiasModel model simulation on a random graph: we set the initial infected node set to the 10% of the overall population.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.opinions as op
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = op.AlgorithmicBiasModel(g)
# Model configuration
config = mc.Configuration()
config.add_model_parameter("epsilon", 0.32)
config.add_model_parameter("gamma", 1)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(200)
Attraction-Repulsion Weighted Hegselmann-Krause¶
The Attraction-Repulsion Weighted Hegselmann-Krause was introduced by Toccaceli et al. in 2020 [1].
This model is a variation of the Weighted Hegselmann-Krause (WHK). This model considers pair-wise interactions. To model the attraction and repulsion of opinions, during each iteration an agent \(i\) is randomly selected along with one of its neighbors, \(j\) - not taking into account the \(\epsilon\) threshold. Once identified the pair-wise interaction, the absolute value of the difference between the opinions of \(i\) and \(j\) is computed. There are four different variants of the method:
- Base case: If the computed difference value is lower than \(\epsilon\) then the update rule becomes:
- Attraction: if the computed difference value is lower than \(\epsilon\) then the following update rule are applied:
where \(sum_{op} = x_i(t) + x_j(t)w_{i,j}\).
- Repulsion: if the difference between \(x_i(t)\) and \(x_j(t)\) exceeds \(\epsilon\) then the following update rule are applied:
where \(sum_{op} = x_i(t) + x_j(t)w_{i,j}\).
4. Attraction and Repulsion: if the computed difference value is lower than \(\epsilon\) then the attraction interaction occurs, otherwise the repulsion attraction occurs.
Node statuses are continuous values in [-1,1].
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
epsilon | Model | float in [0, 1] | — | True | Bounded confidence threshold |
perc_stubborness | Model | float in [0, 1] | 0 | False | Percentage of stubborn agent |
similarity | Model | int in {0, 1} | 0 | False | The method use the feature of the nodes ot not |
option_for_stubbornness | Model | int in {-1,0, 1} | 0 | False | Define distribution of stubborns |
method_variant | Model | int in {0, 1, 2, 3} | 0 | False | The choice of the method to apply |
weight | Edge | float in [0, 1] | 0.1 | False | Edge weight |
stubborn | Node | int in {0, 1} | 0 | False | The agent is stubborn or not |
vector | Node | Vector of float in [0, 1] | [] | False | Vector represents the character of the node |
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.opinions.ARWHKModel.
ARWHKModel
(graph)¶ Model Parameters to be specified via ModelConfig :param epsilon: bounded confidence threshold from the HK model (float in [0,1]) :param perc_stubborness: Percentage of stubborn agent (float in [0,1], default 0) :param option_for_stubbornness: Define distribution of stubborns (in {-1, 0, 1}, default 0) :param similarity: the method uses the similarity or not ( in {0,1}, default 0) :param weight: the weight of edges (float in [0,1]) :param stubborn: The agent is stubborn or not ( in {0,1}, default 0) :param vector: represents the character of the node (list in [0,1], default []) :param method_variant: the variant of method to apply: 0-> base case 1->with attraction 2->with repulsion, 3-> with attractiona nd repulsion ( in {0,1, 2, 3}, default 0)
-
ARWHKModel.
__init__
(graph)¶ Model Constructor :param graph: A networkx graph object
-
ARWHKModel.
set_initial_status
(self, configuration)¶ Override behaviour of methods in class DiffusionModel. Overwrites initial status using random real values.
-
ARWHKModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
ARWHKModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
ARWHKModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
ARWHKModel.
iteration
(self)¶ Execute a single model iteration :return: Iteration_id, Incremental node status (dictionary code -> status)
-
ARWHKModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of an ARWHK model simulation on a random graph: we assign an epsilon value of 0.32, the percentage of stubborness equal 0.2, the distribution of stubborness equal 0 and a weight equal 0.2 to all the edges.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.opinions as opn
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = opn.ARWHKModel(g)
# Model Configuration
config = mc.Configuration()
config.add_model_parameter("epsilon", 0.32)
config.add_model_parameter("perc_stubborness", 0.2)
config.add_model_parameter("option_for_stubbornness", 0)
config.add_model_parameter("method_variant", 2)
# Setting the edge parameters
weight = 0.2
if isinstance(g, nx.Graph):
edges = g.edges
else:
edges = [(g.vs[e.tuple[0]]['name'], g.vs[e.tuple[1]]['name']) for e in g.es]
for e in edges:
config.add_edge_configuration("weight", e, weight)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(20)
[1] |
|
Weighted Hegselmann-Krause¶
The Weighted Hegselmann-Krause was introduced by Milli et al. in 2021 [1].
This model is a variation of the well-known Hegselmann-Krause (HK). During each interaction a random agenti is selected and the set \(\Gamma_{\epsilon}\) of its neighbors whose opinions differ at most \(\epsilon\) (\(d_{i,j}=|x_i(t)-x_j(t)|\leq \epsilon\)) is identified. Moreover, to account for the heterogeneity of interaction frequency among agent pairs, WHK leverages edge weights, thus capturing the effect of different social bonds’ strength/trust as it happens in reality. To such extent, each edge \((i,j) \in E\), carries a value \(w_{i,j}\in [0,1]\). The update rule then becomes:
The idea behind the WHK formulation is that the opinion of agent \(i\) at time \(t+1\), will be given by the combined effect of his previous belief and the average opinion weighed by its, selected, \(\epsilon\)-neighbor, where \(w_{i,j}\) accounts for \(i\)’s perceived influence/trust of \(j\).
Node statuses are continuous values in [-1,1].
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
epsilon | Model | float in [0, 1] | — | True | Bounded confidence threshold |
perc_stubborness | Model | float in [0, 1] | 0 | False | Percentage of stubborn agent |
similarity | Model | int in {0, 1} | 0 | False | The method use the feature of the nodes ot not |
option_for_stubbornness | Model | int in {-1,0, 1} | 0 | False | Define distribution of stubborns |
weight | Edge | float in [0, 1] | 0.1 | False | Edge weight |
stubborn | Node | int in {0, 1} | 0 | False | The agent is stubborn or not |
vector | Node | Vector of float in [0, 1] | [] | False | Vector represents the character of the node |
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.opinions.WHKModel.
WHKModel
(graph)¶ Model Parameters to be specified via ModelConfig :param epsilon: bounded confidence threshold from the HK model (float in [0,1]) :param perc_stubborness: Percentage of stubborn agent (float in [0,1], default 0) :param option_for_stubbornness: Define distribution of stubborns (in {-1,0,1}, default 0) :param similarity: the method uses the similarity or not ( in {0,1}, default 0) :param weight: the weight of edges (float in [0,1]) :param stubborn: The agent is stubborn or not ( in {0,1}, default 0) :param vector: represents the character of the node (list in [0,1], default [])
-
WHKModel.
__init__
(graph)¶ Model Constructor :param graph: A networkx graph object
-
WHKModel.
set_initial_status
(self, configuration)¶ Override behaviour of methods in class DiffusionModel. Overwrites initial status using random real values.
-
WHKModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
WHKModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
WHKModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
WHKModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary code -> status)
-
WHKModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of an WHK model simulation on a random graph: we an epsilon value of 0.32 and a weight equal 0.2 to all the edges.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.opinions as opn
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = opn.WHKModel(g)
# Model Configuration
config = mc.Configuration()
config.add_model_parameter("epsilon", 0.32)
# Setting the edge parameters
weight = 0.2
if isinstance(g, nx.Graph):
edges = g.edges
else:
edges = [(g.vs[e.tuple[0]]['name'], g.vs[e.tuple[1]]['name']) for e in g.es]
for e in edges:
config.add_edge_configuration("weight", e, weight)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(20)
[1] |
|
Hegselmann-Krause¶
The Hegselmann-Krause model was introduced in 2002 by Hegselmann, Krause et al [1].
During each interaction a random agenti is selected and the set \(\Gamma_{\epsilon}\) of its neighbors whose opinions differ at most \(\epsilon\) (\(d_{i,j}=|x_i(t)-x_j(t)|\leq \epsilon\)) is identified. The selected agent i changes its opinion based on the following update rule:
The idea behind the WHK formulation is that the opinion of agent \(i\) at time \(t+1\), will be given by the average opinion by its, selected, \(\epsilon\)-neighbor.
Node statuses are continuous values in [-1,1].
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
epsilon | Model | float in [0, 1] | — | True | Bounded confidence threshold |
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.opinions.HKModel.
HKModel
(graph)¶ Model Parameters to be specified via ModelConfig :param epsilon: bounded confidence threshold from the HK model (float in [0,1])
-
HKModel.
__init__
(graph)¶ Model Constructor :param graph: A networkx graph object
-
HKModel.
set_initial_status
(self, configuration)¶ Override behaviour of methods in class DiffusionModel. Overwrites initial status using random real values.
-
HKModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
HKModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
HKModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
HKModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary code -> status)
-
HKModel.
iteration_bunch
(self, bunch_size)¶ Execute a bunch of model iterations
Parameters: - bunch_size – the number of iterations to execute
- node_status – if the incremental node status has to be returned.
- progress_bar – whether to display a progress bar, default False
Returns: a list containing for each iteration a dictionary {“iteration”: iteration_id, “status”: dictionary_node_to_status}
In the code below is shown an example of instantiation and execution of an HK model simulation on a random graph: we an epsilon value of 0.32 .
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.opinions as opn
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = opn.HKModel(g)
# Model Configuration
config = mc.Configuration()
config.add_model_parameter("epsilon", 0.32)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(20)
[1] |
|
Dynamic Network Models¶
Network topology may evolve as time goes by.
In order to automatically leverage network dynamics NDlib
enables the definition of diffusion models that work on Snapshot Graphs as well as on Interaction Networks.
In particular NDlib
implements dynamic network versions of the following models:
SI¶
The SI model was introduced in 1927 by Kermack [1].
In this model, during the course of an epidemics, a node is allowed to change its status only from Susceptible (S) to Infected (I).
The model is instantiated on a graph having a non-empty set of infected nodes.
SI assumes that if, during a generic iteration, a susceptible node comes into contact with an infected one, it becomes infected with probability β: once a node becomes infected, it stays infected (the only transition allowed is S→I).
The dSI implementation assumes that the process occurs on a directed/undirected dynamic network; this model was introduced by Milli et al. in 2018 [2].
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
beta | Model | float in [0, 1] | True | Infection probability |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.dynamic.DynSIModel.
DynSIModel
(graph, seed=None)¶ Model Parameters to be specified via ModelConfig
Parameters: beta – The infection rate (float value in [0,1])
-
DynSIModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A dynetx graph object
-
DynSIModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
DynSIModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
DynSIModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
DynSIModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
In the code below is shown an example of instantiation and execution of an DynSI simulation on a dynamic random graph: we set the initial set of infected nodes as 5% of the overall population and a probability of infection of 1%.
import networkx as nx
import dynetx as dn
import ndlib.models.ModelConfig as mc
import ndlib.models.dynamic as dm
from past.builtins import xrange
# Dynamic Network topology
dg = dn.DynGraph()
for t in xrange(0, 3):
g = nx.erdos_renyi_graph(200, 0.05)
dg.add_interactions_from(g.edges(), t)
# Model selection
model = dm.DynSIModel(dg)
# Model Configuration
config = mc.Configuration()
config.add_model_parameter('beta', 0.01)
config.add_model_parameter("fraction_infected", 0.1)
model.set_initial_status(config)
# Simulate snapshot based execution
iterations = model.execute_snapshots()
# Simulation interaction graph based execution
iterations = model.execute_iterations()
[1] |
|
[2] | Letizia Milli, Giulio Rossetti, Fosca Giannotti, Dino Pedreschi. “Diffusive Phenomena in Dynamic Networks: a data-driven study”. Accepted to International Conference on Complex Networks (CompleNet), 2018, Boston. |
SIS¶
The SIS model was introduced in 1927 by Kermack [1].
In this model, during the course of an epidemics, a node is allowed to change its status from Susceptible (S) to Infected (I).
The model is instantiated on a graph having a non-empty set of infected nodes.
SIS assumes that if, during a generic iteration, a susceptible node comes into contact with an infected one, it becomes infected with probability beta, than it can be switch again to susceptible with probability lambda (the only transition allowed are S→I→S).
The dSIS implementation assumes that the process occurs on a directed/undirected dynamic network.
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
beta | Model | float in [0, 1] | True | Infection probability | |
lambda | Model | float in [0, 1] | True | Recovery probability |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.dynamic.DynSISModel.
DynSISModel
(graph, seed=None)¶ Model Parameters to be specified via ModelConfig
Parameters: - beta – The infection rate (float value in [0,1])
- lambda – The recovery rate (float value in [0,1])
-
DynSISModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
DynSISModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
DynSISModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
DynSISModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
DynSISModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
In the code below is shown an example of instantiation and execution of an DynSIS simulation on a dynamic random graph: we set the initial set of infected nodes as 5% of the overall population, a probability of infection of 1%, and a probability of recovery of 1%.
import networkx as nx
import dynetx as dn
import ndlib.models.ModelConfig as mc
import ndlib.models.dynamic as dm
from past.builtins import xrange
# Dynamic Network topology
dg = dn.DynGraph()
for t in xrange(0, 3):
g = nx.erdos_renyi_graph(200, 0.05)
dg.add_interactions_from(g.edges(), t)
# Model selection
model = dm.DynSISModel(dg)
# Model Configuration
config = mc.Configuration()
config.add_model_parameter('beta', 0.01)
config.add_model_parameter('lambda', 0.01)
config.add_model_parameter("fraction_infected", 0.1)
model.set_initial_status(config)
# Simulate snapshot based execution
iterations = model.execute_snapshots()
# Simulation interaction graph based execution
iterations = model.execute_iterations()
[1] |
|
SIR¶
The SIR model was introduced in 1927 by Kermack [1].
In this model, during the course of an epidemics, a node is allowed to change its status from Susceptible (S) to Infected (I), then to Removed (R).
The model is instantiated on a graph having a non-empty set of infected nodes.
SIR assumes that if, during a generic iteration, a susceptible node comes into contact with an infected one, it becomes infected with probability beta, than it can be switch to removed with probability gamma (the only transition allowed are S→I→R).
The dSIR implementation assumes that the process occurs on a directed/undirected dynamic network; this model was introduced by Milli et al. in 2018 [2].
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Removed | 2 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
beta | Model | float in [0, 1] | True | Infection probability | |
gamma | Model | float in [0, 1] | True | Removal probability |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.dynamic.DynSIRModel.
DynSIRModel
(graph, seed=None)¶ Model Parameters to be specified via ModelConfig
Parameters: - beta – The infection rate (float value in [0,1])
- gamma – The recovery rate (float value in [0,1])
-
DynSIRModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
DynSIRModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
DynSIRModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
DynSIRModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
DynSIRModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
In the code below is shown an example of instantiation and execution of an DynSIR simulation on a dynamic random graph: we set the initial set of infected nodes as 5% of the overall population, a probability of infection of 1%, and a removal probability of 1%.
import networkx as nx
import dynetx as dn
import ndlib.models.ModelConfig as mc
import ndlib.models.dynamic as dm
from past.builtins import xrange
# Dynamic Network topology
dg = dn.DynGraph()
for t in xrange(0, 3):
g = nx.erdos_renyi_graph(200, 0.05)
dg.add_interactions_from(g.edges(), t)
# Model selection
model = dm.DynSIRModel(dg)
# Model Configuration
config = mc.Configuration()
config.add_model_parameter('beta', 0.01)
config.add_model_parameter('gamma', 0.01)
config.add_model_parameter("fraction_infected", 0.1)
model.set_initial_status(config)
# Simulate snapshot based execution
iterations = model.execute_snapshots()
# Simulation interaction graph based execution
iterations = model.execute_iterations()
[1] |
|
[2] | Letizia Milli, Giulio Rossetti, Fosca Giannotti, Dino Pedreschi. “Diffusive Phenomena in Dynamic Networks: a data-driven study”. Accepted to International Conference on Complex Networks (CompleNet), 2018, Boston. |
Kertesz Threshold¶
The Kertesz Threshold model was introduced in 2015 by Ruan et al. [1] and it is an extension of the Watts threshold model [2].
The authors extend the classical model introducing a density r of blocked nodes – nodes which are immune to social influence – and a probability of spontaneous adoption p to capture external influence.
Thus, the model distinguishes three kinds of node: Blocked (B), Susceptible (S) and Adoptiong (A). The latter class breaks into two categories: vulnerable and stable nodes. A node can adopt either under its neighbors’ influence, or spontaneously, due to endogenous effects.
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Blocked | -1 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
adopter_rate | Model | float in [0, 1] | 0 | False | Exogenous adoption rate |
percentage_blocked | Model | float in [0, 1] | 0.1 | False | Blocked nodes |
threshold | Node | float in [0, 1] | 0.1 | False | Individual threshold |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The initial blocked nodes can be defined via:
- percentage_blocked: Model Parameter, float in [0, 1]
- Blocked: Status Parameter, set of nodes
In both cases, the two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.dynamic.DynKerteszThresholdModel.
DynKerteszThresholdModel
(graph, seed=None)¶ - Node Parameters to be specified via ModelConfig
Parameters: profile – The node profile. As default a value of 0.1 is assumed for all nodes.
-
DynKerteszThresholdModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
DynKerteszThresholdModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
DynKerteszThresholdModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
DynKerteszThresholdModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
DynKerteszThresholdModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
In the code below is shown an example of instantiation and execution of a Kertesz Threshold model simulation on a random graph: we set the initial infected as well blocked node sets equals to the 10% of the overall population, assign a threshold of 0.25 to all the nodes and impose an probability of spontaneous adoptions of 40%.
import networkx as nx
import dynetx as dn
import ndlib.models.ModelConfig as mc
import ndlib.models.dynamic as dm
# Dynamic Network topology
dg = dn.DynGraph()
for t in past.builtins.xrange(0, 3):
g = nx.erdos_renyi_graph(200, 0.05)
dg.add_interactions_from(g.edges(), t)
# Model selection
model = dm.DynKerteszThresholdModel(g)
# Model Configuration
config = mc.Configuration()
config.add_model_parameter('adopter_rate', 0.4)
config.add_model_parameter('percentage_blocked', 0.1)
config.add_model_parameter('fraction_infected', 0.1)
# Setting node parameters
threshold = 0.25
for i in g.nodes():
config.add_node_configuration("threshold", i, threshold)
model.set_initial_status(config)
# Simulate snapshot based execution
iterations = model.execute_snapshots()
[1] |
|
[2] |
|
Profile¶
The Profile model, introduced by Milli et al. in [1], assumes that the diffusion process is only apparent; each node decides to adopt or not a given behavior – once known its existence – only on the basis of its own interests.
In this scenario the peer pressure is completely ruled out from the overall model: it is not important how many of its neighbors have adopted a specific behaviour, if the node does not like it, it will not change its interests.
Each node has its own profile describing how many it is likely to accept a behaviour similar to the one that is currently spreading.
The diffusion process starts from a set of nodes that have already adopted a given behaviour S:
- for each of the susceptible nodes’ in the neighborhood of a node u in S an unbalanced coin is flipped, the unbalance given by the personal profile of the susceptible node;
- if a positive result is obtained the susceptible node will adopt the behaviour, thus becoming infected.
- if the blocked status is enabled, after having rejected the adoption with probability
blocked
a node becomes immune to the infection. - every iteration
adopter_rate
percentage of nodes spontaneous became infected to endogenous effects.
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Blocked | -1 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
profile | Node | float in [0, 1] | 0.1 | False | Node profile |
blocked | Model | float in [0, 1] | 0 | False | Blocked nodes |
adopter_rate | Model | float in [0, 1] | 0 | False | Autonomous adoption |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.dynamic.DynProfileModel.
DynProfileModel
(graph, seed=None)¶ - Node Parameters to be specified via ModelConfig
Parameters: profile – The node profile. As default a value of 0.1 is assumed for all nodes.
-
DynProfileModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
DynProfileModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
DynProfileModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
DynProfileModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
DynProfileModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
DynProfileModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
DynProfileModel.
execute_snapshots
(bunch_size, node_status)¶
NB: the ``execute_iterations()`` method is unavailable for this model (along with other thresholded models).
In the code below is shown an example of instantiation and execution of a Profile model simulation on a random graph: we set the initial infected node set to the 10% of the overall population and assign a profile of 0.25 to all the nodes.
import networkx as nx
import dynetx as dn
import ndlib.models.ModelConfig as mc
import ndlib.models.dynamic as dm
from past.builtins import xrange
# Dynamic Network topology
dg = dn.DynGraph()
for t in xrange(0, 3):
g = nx.erdos_renyi_graph(200, 0.05)
dg.add_interactions_from(g.edges(), t)
# Model selection
model = dm.DynProfileModel(dg)
config = mc.Configuration()
config.add_model_parameter('blocked', 0)
config.add_model_parameter('adopter_rate', 0)
config.add_model_parameter('fraction_infected', 0.1)
# Setting nodes parameters
profile = 0.15
for i in g.nodes():
config.add_node_configuration("profile", i, profile)
model.set_initial_status(config)
# Simulate snapshot based execution
iterations = model.execute_snapshots()
[1] | Milli, L., Rossetti, G., Pedreschi, D., & Giannotti, F. (2018). Active and passive diffusion processes in complex networks. Applied network science, 3(1), 42. |
Threshold¶
The Profile-Threshold model, introduced by Milli et al. in [1], assumes the existence of node profiles that act as preferential schemas for individual tastes but relax the constraints imposed by the Profile model by letting nodes influenceable via peer pressure mechanisms.
The peer pressure is modeled with a threshold.
The diffusion process starts from a set of nodes that have already adopted a given behaviour S:
- for each of the susceptible node an unbalanced coin is flipped if the percentage of its neighbors that are already infected excedes its threhosld. As in the Profile Model the coin unbalance is given by the personal profile of the susceptible node;
- if a positive result is obtained the susceptible node will adopt the behaviour, thus becoming infected.
- if the blocked status is enabled, after having rejected the adoption with probability
blocked
a node becomes immune to the infection. - every iteration
adopter_rate
percentage of nodes spontaneous became infected to endogenous effects.
During the simulation a node can experience the following statuses:
Name | Code |
---|---|
Susceptible | 0 |
Infected | 1 |
Blocked | -1 |
Name | Type | Value Type | Default | Mandatory | Description |
---|---|---|---|---|---|
threshold | Node | float in [0, 1] | 0.1 | False | Individual threshold |
profile | Node | float in [0, 1] | 0.1 | False | Node profile |
blocked | Model | float in [0, 1] | 0 | False | Blocked nodes |
adopter_rate | Model | float in [0, 1] | 0 | False | Autonomous adoption |
The initial infection status can be defined via:
- fraction_infected: Model Parameter, float in [0, 1]
- Infected: Status Parameter, set of nodes
The two options are mutually exclusive and the latter takes precedence over the former.
The following class methods are made available to configure, describe and execute the simulation:
-
class
ndlib.models.dynamic.DynProfileThresholdModel.
DynProfileThresholdModel
(graph, seed=None)¶ Node Parameters to be specified via ModelConfig
Parameters: - profile – The node profile. As default a value of 0.1 is assumed for all nodes.
- threshold – The node threshold. As default a value of 0.1 is assumed for all nodes.
-
DynProfileThresholdModel.
__init__
(graph)¶ Model Constructor
Parameters: graph – A networkx graph object
-
DynProfileThresholdModel.
set_initial_status
(self, configuration)¶ Set the initial model configuration
Parameters: configuration – a `ndlib.models.ModelConfig.Configuration`
object
-
DynProfileThresholdModel.
reset
(self)¶ Reset the simulation setting the actual status to the initial configuration.
-
DynProfileThresholdModel.
get_info
(self)¶ Describes the current model parameters (nodes, edges, status)
Returns: a dictionary containing for each parameter class the values specified during model configuration
-
DynProfileThresholdModel.
get_status_map
(self)¶ Specify the statuses allowed by the model and their numeric code
Returns: a dictionary (status->code)
-
DynProfileThresholdModel.
iteration
(self)¶ Execute a single model iteration
Returns: Iteration_id, Incremental node status (dictionary node->status)
-
DynProfileThresholdModel.
execute_snapshots
(bunch_size, node_status)¶
NB: the ``execute_iterations()`` method is unavailable for this model (along with other thresholded models).
In the code below is shown an example of instantiation and execution of a Profile Threshold model simulation on a random graph: we set the initial infected node set to the 10% of the overall population, assign a profile of 0.25 and a threshold of 0.15 to all the nodes.
import networkx as nx
import dynetx as dn
import ndlib.models.ModelConfig as mc
import ndlib.models.dynamic as dm
from past.builtins import xrange
# Dynamic Network topology
dg = dn.DynGraph()
for t in xrange(0, 3):
g = nx.erdos_renyi_graph(200, 0.05)
dg.add_interactions_from(g.edges(), t)
# Model selection
model = dm.DynProfileThresholdModel(dg)
config = mc.Configuration()
config.add_model_parameter('blocked', 0)
config.add_model_parameter('adopter_rate', 0)
config.add_model_parameter('fraction_infected', 0.1)
# Setting nodes parameters
threshold = 0.15
profile = 0.25
for i in g.nodes():
config.add_node_configuration("threshold", i, threshold)
config.add_node_configuration("profile", i, profile)
model.set_initial_status(config)
# Simulate snapshot based execution
iterations = model.execute_snapshots()
[1] | Milli, L., Rossetti, G., Pedreschi, D., & Giannotti, F. (2018). Active and passive diffusion processes in complex networks. Applied network science, 3(1), 42. |
Model Configuration¶
NDlib
adopts a peculiar approach to specify the configuration of expetiments.
It employs a centralyzed system that take care of:
- Describe a common syntax for model configuration;
- Provide an interface to set the initial conditions of an experiment (nodes/edges properties, initial nodes statuses)
ModelConfig¶
The ModelConfig
object is the common interface used to set up simulation experiments.
-
class
ndlib.models.ModelConfig.
Configuration
¶ Configuration Object
It allows to specify four categories of experiment configurations:
- Model configuration
- Node Configuration
- Edge Configuration
- Initial Status
Every diffusion model has its own parameters (as defined in its reference page).
Model Configuration¶
Model configuration involves the instantiation of both the mandatory and optional parameters of the chosen diffusion model.
-
Configuration.
add_model_parameter
(self, param_name, param_value)¶ Set a Model Parameter
Parameters: - param_name – parameter identifier (as specified by the chosen model)
- param_value – parameter value
Model parameters can be setted as in the following example:
import ndlib.models.ModelConfig as mc
# Model Configuration
config = mc.Configuration()
config.add_model_parameter("beta", 0.15)
The only model parameter common to all the diffusive approaches is fraction_infected
that allows to specify the ratio of infected nodes at the beginning of the simulation.
Node Configuration¶
Node configuration involves the instantiation of both the mandatory and optional parameters attached to individual nodes.
-
Configuration.
add_node_configuration
(self, param_name, node_id, param_value)¶ Set a parameter for a given node
Parameters: - param_name – parameter identifier (as specified by the chosen model)
- node_id – node identifier
- param_value – parameter value
-
Configuration.
add_node_set_configuration
(self, param_name, node_to_value)¶ Set Nodes parameter
Parameters: - param_name – parameter identifier (as specified by the chosen model)
- node_to_value – dictionary mapping each node a parameter value
Node parameters can be set as in the following example:
import ndlib.models.ModelConfig as mc
# Model Configuration
config = mc.Configuration()
threshold = 0.25
for i in g.nodes():
config.add_node_configuration("threshold", i, threshold)
Edge Configuration¶
Edge configuration involves the instantiation of both the mandatory and optional parameters attached to individual edges.
-
Configuration.
add_edge_configuration
(self, param_name, edge, param_value)¶ Set a parameter for a given edge
Parameters: - param_name – parameter identifier (as specified by the chosen model)
- edge – edge identifier
- param_value – parameter value
-
Configuration.
add_edge_set_configuration
(self, param_name, edge_to_value)¶ Set Edges parameter
Parameters: - param_name – parameter identifier (as specified by the chosen model)
- edge_to_value – dictionary mapping each edge a parameter value
Edge parameters can be set as in the following example:
import ndlib.models.ModelConfig as mc
# Model Configuration
config = mc.Configuration()
threshold = 0.25
for i in g.nodes():
config.add_edge_configuration("threshold", i, threshold)
Status Configuration¶
Status configuration allows to specify explicitly the status of a set of nodes at the beginning of the simulation.
-
Configuration.
add_model_initial_configuration
(self, status_name, nodes)¶ Set initial status for a set of nodes
Parameters: - status_name – status to be set (as specified by the chosen model)
- nodes – list of affected nodes
Node statuses can be set as in the following example:
import ndlib.models.ModelConfig as mc
# Model Configuration
config = mc.Configuration()
infected_nodes = [0, 1, 2, 3, 4, 5]
config.add_model_initial_configuration("Infected", infected_nodes)
Explicit status specification takes priority over the percentage specification expressed via model definition (e.g. fraction_infected
).
Only the statuses implemented by the chosen model can be used to specify initial configurations of nodes.
NDlib Utils¶
The ndlib.utils
module contains facilities that extend the simulation framework (i.e., automated multiple executions).
Model Multiple Executions¶
dlib.utils.multi_runs
allows the parallel execution of multiple instances of a given model starting from different initial infection conditions.
The initial infected nodes for each instance of the model can be specified either:
- by the “fraction_infected” model parameter, or
- explicitly through a list of
n
sets of nodes (wheren
is the number of executions required).
In the first scenario “fraction_infected” nodes will be sampled independently for each model execution.
Results of dlib.utils.multi_runs
can be feed directly to all the visualization facilities exposed by ndlib.viz
.
-
ndlib.utils.
multi_runs
(model, execution_number, iteration_number, infection_sets, nprocesses)¶ Multiple executions of a given model varying the initial set of infected nodes
Parameters: - model – a configured diffusion model
- execution_number – number of instantiations
- iteration_number – number of iterations per execution
- infection_sets – predefined set of infected nodes sets
- nprocesses – number of processes. Default values cpu number.
Returns: resulting trends for all the executions
Example¶
Randomly selection of initial infection sets
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
from ndlib.utils import multi_runs
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model1 = ep.SIRModel(g)
# Model Configuration
config = mc.Configuration()
config.add_model_parameter('beta', 0.001)
config.add_model_parameter('gamma', 0.01)
config.add_model_parameter("fraction_infected", 0.05)
model1.set_initial_status(config)
# Simulation multiple execution
trends = multi_runs(model1, execution_number=10, iteration_number=100, infection_sets=None, nprocesses=4)
Specify initial infection sets
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
from ndlib.utils import multi_runs
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model1 = ep.SIRModel(g)
# Model Configuration
config = mc.Configuration()
config.add_model_parameter('beta', 0.001)
config.add_model_parameter('gamma', 0.01)
model1.set_initial_status(config)
# Simulation multiple execution
infection_sets = [(1, 2, 3, 4, 5), (3, 23, 22, 54, 2), (98, 2, 12, 26, 3), (4, 6, 9) ]
trends = multi_runs(model1, execution_number=2, iteration_number=100, infection_sets=infection_sets, nprocesses=4)
Plot multiple executions
The ndlib.viz.mpl
package offers support for visualization of multiple runs.
In order to visualize the average trend/prevalence along with its inter-percentile range use the following pattern (assuming model1
and trends
be the results of the previous code snippet).
from ndlib.viz.mpl.DiffusionTrend import DiffusionTrend
viz = DiffusionTrend(model1, trends)
viz.plot("diffusion.pdf", percentile=90)
where percentile
identifies the upper and lower bound (e.g. setting it to 90 implies a range 10-90).
The same pattern can be also applied to comparison plots.
Visualization¶
In order to provide an easy proxy to study diffusion phenomena and compare different configurations as well as models NDlib
offers built-in visualization facilities.
In particular, the following plots are made available:
Pyplot Viz¶
Classic Visualizations
Diffusion Trend¶
The Diffusion Trend plot compares the trends of all the statuses allowed by the diffusive model tested.
Each trend line describes the variation of the number of nodes for a given status iteration after iteration.
-
class
ndlib.viz.mpl.DiffusionTrend.
DiffusionTrend
(model, trends)¶
-
DiffusionTrend.
__init__
(model, trends)¶ Parameters: - model – The model object
- trends – The computed simulation trends
-
DiffusionTrend.
plot
(filename, percentile)¶ Generates the plot
Parameters: - filename – Output filename
- percentile – The percentile for the trend variance area
- statuses – List of statuses to plot. If not specified all statuses trends will be shown.
Below is shown an example of Diffusion Trend description and visualization for the SIR model.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
from ndlib.viz.mpl.DiffusionTrend import DiffusionTrend
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.SIRModel(g)
# Model Configuration
cfg = mc.Configuration()
cfg.add_model_parameter('beta', 0.001)
cfg.add_model_parameter('gamma', 0.01)
cfg.add_model_parameter("fraction_infected", 0.01)
model.set_initial_status(cfg)
# Simulation execution
iterations = model.iteration_bunch(200)
trends = model.build_trends(iterations)
# Visualization
viz = DiffusionTrend(model, trends)
viz.plot("diffusion.pdf")
Diffusion Prevalence¶
The Diffusion Prevalence plot compares the delta-trends of all the statuses allowed by the diffusive model tested.
Each trend line describes the delta of the number of nodes for a given status iteration after iteration.
-
class
ndlib.viz.mpl.DiffusionPrevalence.
DiffusionPrevalence
(model, trends)¶
-
DiffusionPrevalence.
__init__
(model, trends)¶ Parameters: - model – The model object
- trends – The computed simulation iterations
-
DiffusionPrevalence.
plot
(filename, percentile)¶ Generates the plot
Parameters: - filename – Output filename
- percentile – The percentile for the trend variance area
- statuses – List of statuses to plot. If not specified all statuses trends will be shown.
Below is shown an example of Diffusion Prevalence description and visualization for the SIR model.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
from ndlib.viz.mpl.DiffusionPrevalence import DiffusionPrevalence
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.SIRModel(g)
# Model Configuration
cfg = mc.Configuration()
cfg.add_model_parameter('beta', 0.001)
cfg.add_model_parameter('gamma', 0.01)
cfg.add_model_parameter("fraction_infected", 0.01)
model.set_initial_status(cfg)
# Simulation execution
iterations = model.iteration_bunch(200)
trends = model.build_trends(iterations)
# Visualization
viz = DiffusionPrevalence(model, trends)
viz.plot("prevalence.pdf")
Opinion Evolution¶
The Opinion Evolution plot shows the node-wise opinion evolution in a continuous states model.
-
class
ndlib.viz.mpl.OpinionEvolution.
OpinionEvolution
(model, trends)¶
-
OpinionEvolution.
__init__
(model, trends)¶ Parameters: - model – The model object
- trends – The computed simulation trends
-
OpinionEvolution.
plot
(filename)¶ Generates the plot
Parameters: - filename – Output filename
- percentile – The percentile for the trend variance area
Below is shown an example of Opinion Evolution description and visualization for the Algorithmic Bias model.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.opinions as op
from ndlib.viz.mpl.OpinionEvolution import OpinionEvolution
# mMean field scenario
g = nx.complete_graph(100)
# Algorithmic Bias model
model = op.AlgorithmicBiasModel(g)
# Model configuration
config = mc.Configuration()
config.add_model_parameter("epsilon", 0.32)
config.add_model_parameter("gamma", 0)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(100)
viz = OpinionEvolution(model, iterations)
viz.plot("opinion_ev.pdf")
Model Comparison Visualizations
Diffusion Trend Comparison¶
The Diffusion Trend Comparison plot compares the trends of all the statuses allowed by the diffusive model tested.
Each trend line describes the variation of the number of nodes for a given status iteration after iteration.
-
class
ndlib.viz.mpl.TrendComparison.
DiffusionTrendComparison
(models, trends, statuses='Infected')¶
-
DiffusionTrendComparison.
__init__
(models, trends, statuses)¶ Parameters: - models – A list of model object
- trends – A list of computed simulation trends
- statuses – The model statuses for which make the plot. Default [“Infected”].
-
DiffusionTrendComparison.
plot
(filename, percentile)¶ Plot the comparison on file.
Parameters: - filename – the output filename
- percentile – The percentile for the trend variance area. Default 90.
Below is shown an example of Diffusion Trend description and visualization for the SIR model.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
from ndlib.viz.mpl.TrendComparison import DiffusionTrendComparison
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.SIRModel(g)
# Model Configuration
cfg = mc.Configuration()
cfg.add_model_parameter('beta', 0.001)
cfg.add_model_parameter('gamma', 0.01)
cfg.add_model_parameter("fraction_infected", 0.01)
model.set_initial_status(cfg)
# Simulation execution
iterations = model.iteration_bunch(200)
trends = model.build_trends(iterations)
# 2° Model selection
model1 = ep.SIRModel(g)
# 2° Model Configuration
cfg = mc.Configuration()
cfg.add_model_parameter('beta', 0.001)
cfg.add_model_parameter('gamma', 0.02)
cfg.add_model_parameter("fraction_infected", 0.01)
model1.set_initial_status(cfg)
# 2° Simulation execution
iterations = model1.iteration_bunch(200)
trends1 = model1.build_trends(iterations)
# Visualization
viz = DiffusionTrend([model, model1], [trends, trends1])
viz.plot("trend_comparison.pdf")
Diffusion Prevalence Comparison¶
The Diffusion Prevalence plot compares the delta-trends of all the statuses allowed by the diffusive model tested.
Each trend line describes the delta of the number of nodes for a given status iteration after iteration.
-
class
ndlib.viz.mpl.PrevalenceComparison.
DiffusionPrevalenceComparison
(models, trends, statuses='Infected')¶
-
DiffusionPrevalenceComparison.
__init__
(model, trends)¶ Parameters: - models – A list of model object
- trends – A list of computed simulation trends
- statuses – The model statuses for which make the plot. Default [“Infected”].
-
DiffusionPrevalenceComparison.
plot
(filename, percentile)¶ Plot the comparison on file.
Parameters: - filename – the output filename
- percentile – The percentile for the trend variance area. Default 90.
Below is shown an example of Diffusion Prevalence description and visualization for two instances of the SIR model.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
from ndlib.viz.mpl.PrevalenceComparison import DiffusionPrevalenceComparison
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.SIRModel(g)
# Model Configuration
cfg = mc.Configuration()
cfg.add_model_parameter('beta', 0.001)
cfg.add_model_parameter('gamma', 0.02)
cfg.add_model_parameter("fraction_infected", 0.01)
model.set_initial_status(cfg)
# Simulation execution
iterations = model.iteration_bunch(200)
trends = model.build_trends(iterations)
# 2° Model selection
model1 = ep.SIModel(g)
# 2° Model Configuration
cfg = mc.Configuration()
cfg.add_model_parameter('beta', 0.001)
cfg.add_model_parameter("fraction_infected", 0.01)
model1.set_initial_status(cfg)
# 2° Simulation execution
iterations = model1.iteration_bunch(200)
trends1 = model1.build_trends(iterations)
# Visualization
viz = DiffusionPrevalenceComparison([model, model1], [trends, trends1])
viz.plot("trend_comparison.pdf")
Bokeh Viz¶
Classic Visualizations
Diffusion Trend¶
The Diffusion Trend plot compares the trends of all the statuses allowed by the diffusive model tested.
Each trend line describes the variation of the number of nodes for a given status iteration after iteration.
-
class
ndlib.viz.bokeh.DiffusionTrend.
DiffusionTrend
(model, trends)¶
-
DiffusionTrend.
__init__
(model, iterations)¶ Parameters: - model – The model object
- iterations – The computed simulation iterations
-
DiffusionTrend.
plot
(width, height)¶ Generates the plot
Parameters: - percentile – The percentile for the trend variance area
- width – Image width. Default 500px.
- height – Image height. Default 500px.
Returns: a bokeh figure image
Below is shown an example of Diffusion Trend description and visualization for the SIR model.
import networkx as nx
from bokeh.io import show
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
from ndlib.viz.bokeh.DiffusionTrend import DiffusionTrend
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.SIRModel(g)
# Model Configuration
cfg = mc.Configuration()
cfg.add_model_parameter('beta', 0.001)
cfg.add_model_parameter('gamma', 0.01)
cfg.add_model_parameter("fraction_infected", 16 0.05)
model.set_initial_status(cfg)
# Simulation execution
iterations = model.iteration_bunch(200)
trends = model.build_trends(iterations)
# Visualization
viz = DiffusionTrend(model, trends)
p = viz.plot(width=400, height=400)
show(p)
Diffusion Prevalence¶
The Diffusion Prevalence plot compares the delta-trends of all the statuses allowed by the diffusive model tested.
Each trend line describes the delta of the number of nodes for a given status iteration after iteration.
-
class
ndlib.viz.bokeh.DiffusionPrevalence.
DiffusionPrevalence
(model, trends)¶
-
DiffusionPrevalence.
__init__
(model, iterations)¶ Parameters: - model – The model object
- iterations – The computed simulation iterations
-
DiffusionPrevalence.
plot
(width, height)¶ Generates the plot
Parameters: - percentile – The percentile for the trend variance area
- width – Image width. Default 500px.
- height – Image height. Default 500px.
Returns: a bokeh figure image
Below is shown an example of Diffusion Prevalence description and visualization for the SIR model.
import networkx as nx
from bokeh.io import show
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
from ndlib.viz.bokeh.DiffusionPrevalence import DiffusionPrevalence
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.SIRModel(g)
# Model Configuration
cfg = mc.Configuration()
cfg.add_model_parameter('beta', 0.001)
cfg.add_model_parameter('gamma', 0.01)
cfg.add_model_parameter("fraction_infected", 16 0.05)
model.set_initial_status(cfg)
# Simulation execution
iterations = model.iteration_bunch(200)
trends = model.build_trends(iterations)
# Visualization
viz = DiffusionPrevalence(model, trends)
p = viz.plot(width=400, height=400)
show(p)
Model Comparison Visualizations
Multi Plot¶
The Multi Plot object allows the generation of composite grid figures composed by multiple Diffusion Trends and/or Diffusion Prevalence plots.
-
class
ndlib.viz.bokeh.MultiPlot.
MultiPlot
¶
-
MultiPlot.
add_plot
(plot)¶ Parameters: plot – The bokeh plot to add to the grid
-
MultiPlot.
plot
(width, height)¶ Parameters: ncols – Number of grid columns Returns: a bokeh figure image
import networkx as nx
from bokeh.io import show
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics as ep
from ndlib.viz.bokeh.DiffusionTrend import DiffusionTrend
from ndlib.viz.bokeh.DiffusionPrevalence import DiffusionPrevalence
from ndlib.viz.bokeh.MultiPlot import Multiplot
vm = MultiPlot()
# Network topology
g = nx.erdos_renyi_graph(1000, 0.1)
# Model selection
model = ep.SIRModel(g)
# Model Configuration
cfg = mc.Configuration()
cfg.add_model_parameter('beta', 0.001)
cfg.add_model_parameter('gamma', 0.01)
cfg.add_model_parameter("fraction_infected", 16 0.05)
model.set_initial_status(cfg)
# Simulation execution
iterations = model.iteration_bunch(200)
trends = model.build_trends(iterations)
# Diffusion Trend
viz = DiffusionTrend(model, trends)
p = viz.plot(width=400, height=400)
vm.add_plot(p)
# Diffusion Prevalence
viz = DiffusionPrevalence(model, trends)
p1 = viz.plot(width=400, height=400)
vm.add_plot(p1)
m = vm.plot(ncol=2)
show(m)
[1] |
|
[2] |
|
[3] | P.Wang,M.C.Gonzalez,R.Menezes,andA.L.Baraba ́si,“Understanding the spread of malicious mobile-phone programs and their damage potential,” International Journal of Information Security, 2013. |
[4] |
|
[5] |
|
[6] |
|
[7] |
|
[8] |
|
Custom Model Definition¶
NDlib
exposes a set of built-in diffusion models (epidemic/opinion dynamics/dynamic network): how can I describe novel ones?
In order to answer such question we developed a syntax for compositional model definition.
Rationale¶
At a higher level of abstraction a diffusion process can be synthesized into two components:
- Available Statuses, and
- Transition Rules that connect them
All models of NDlib
assume an agent-based, discrete time, simulation engine.
During each simulation iteration all the nodes in the network are asked to (i) evaluate their current status and to (ii) (eventually) apply a matching transition rule.
The last step of such process can be easily decomposed into atomic operations that we will call compartments.
Note
NDlib
exposes three classes for defining custom diffusion models:
CompositeModel
describes diffusion models for static networksDynamicCompositeModel
describes diffusion models for dynamic networksContinuousModel
describes diffusion models with continuous states for static and dynamic networks
To avoid redundant documentation, here we will discuss only the former class, the second behaving alike. The ContinuousModel
class will have a seperate section due to its extra complexity.
Compartments¶
We adopt the concept of compartment
to identify all those atomic conditions (i.e. operations) that describe (part of) a transition rule.
The execution of a compartment
can return either True (condition satisfied) or False (condition not satisfied).
Indeed, several compartments can be described, each one of them capturing an atomic operation.
To cover the main scenarios we defined three families of compartments as well as some operations to combine them.
Node Compartments¶
In this class fall all those compartments that evaluate conditions tied to node status/features. They model stochastic events as well as deterministic ones.
Node Stochastic¶
Node Stochastic compartments are used to evaluate stochastic events attached to network nodes.
Consider the transition rule Susceptible->Infected that requires a probability beta to be satisfied. Such rule can be described by a simple compartment that models Node Stochastic behaviors. Let’s call il NS.
The rule will take as input the initial node status (Susceptible), the final one (Infected) and the NS compartment. NS will thus require a probability (beta) of activation.
During each rule evaluation, given a node n
- if the actual status of n equals the rule initial one
- a random value b in [0,1] will be generated
- if b <= beta then NS is considered satisfied and the status of n changes from initial to final.
Moreover, NS allows to specify a triggering status in order to restrain the compartment evaluation to those nodes that:
- match the rule initial state, and
- have at least one neighbors in the triggering status.
Name | Value Type | Default | Mandatory | Description |
---|---|---|---|---|
ratio | float in [0, 1] | True | Event probability | |
triggering_status | string | None | False | Trigger |
In the code below is shown the formulation of a SIR model using NodeStochastic compartments.
The first compartment, c1, is used to implement the transition rule Susceptible->Infected. It requires a probability threshold - here set equals to 0.02 - and restrain the rule evaluation to all those nodes that have at least an Infected neighbors.
The second compartment, c2, is used to implement the transition rule Infected->Removed. Since such transition is not tied to neighbors statuses the only parameter required by the compartment is the probability of transition.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.CompositeModel as gc
import ndlib.models.compartments as cpm
# Network generation
g = nx.erdos_renyi_graph(1000, 0.1)
# Composite Model instantiation
model = gc.CompositeModel(g)
# Model statuses
model.add_status("Susceptible")
model.add_status("Infected")
model.add_status("Removed")
# Compartment definition
c1 = cpm.NodeStochastic(0.02, triggering_status="Infected")
c2 = cpm.NodeStochastic(0.01)
# Rule definition
model.add_rule("Susceptible", "Infected", c1)
model.add_rule("Infected", "Removed", c2)
# Model initial status configuration
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)
# Simulation execution
model.set_initial_status(config)
iterations = model.iteration_bunch(100)
Node Categorical Attribute¶
Node Categorical Attribute compartments are used to evaluate events attached to network nodes attributes.
Consider the transition rule Susceptible->Infected that requires a that the susceptible node express a specific value of an internal attribute, attr, to be satisfied (e.g. “Sex”=”male”). Such rule can be described by a simple compartment that models Node Categorical Attribute selection. Let’s call il NCA.
The rule will take as input the initial node status (Susceptible), the final one (Infected) and the NCA compartment. NCA will thus require a probability (beta) of activation.
During each rule evaluation, given a node n
- if the actual status of n equals the rule initial one
- a random value b in [0,1] will be generated
- if b <= beta and attr(n) == attr, then NCA is considered satisfied and the status of n changes from initial to final.
Name | Value Type | Default | Mandatory | Description |
---|---|---|---|---|
attribute | string | None | True | Attribute name |
value | string | None | True | Attribute testing value |
probability | float in [0, 1] | 1 | False | Event probability |
In the code below is shown the formulation of a model using NodeCategoricalAttribute compartments.
The compartment, c1, is used to implement the transition rule Susceptible->Infected. It restrain the rule evaluation to all those nodes for which the attribute “Sex” equals “male”.
import networkx as nx
import random
import ndlib.models.ModelConfig as mc
import ndlib.models.CompositeModel as gc
import ndlib.models.compartments as cpm
# Network generation
g = nx.erdos_renyi_graph(1000, 0.1)
# Setting node attribute
attr = {n: {"Sex": random.choice(['male', 'female'])} for n in g.nodes()}
nx.set_node_attributes(g, attr)
# Composite Model instantiation
model = gc.CompositeModel(g)
# Model statuses
model.add_status("Susceptible")
model.add_status("Infected")
model.add_status("Removed")
# Compartment definition
c1 = cpm.NodeCategoricalAttribute("Sex", "male", probability=0.6)
# Rule definition
model.add_rule("Susceptible", "Infected", c1)
# Model initial status configuration
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0)
# Simulation execution
model.set_initial_status(config)
iterations = model.iteration_bunch(100)
Node Numerical Attribute¶
Node Numerical Attribute compartments are used to evaluate events attached to numeric edge attributes.
Consider the transition rule Susceptible->Infected that requires that the susceptible node expresses a specific value of an internal numeric attribute, attr, to be satisfied (e.g. “Age” == 18). Such a rule can be described by a simple compartment that models Node Numerical Attribute selection. Let’s call it NNA.
The rule will take as input the initial node status (Susceptible), the final one (Infected) and the NNA compartment. NNA will thus require a probability (beta) of activation.
During each rule evaluation, given a node n and one of its neighbors m
- if the actual status of n equals the rule initial
- if attr(n) op attr
- a random value b in [0,1] will be generated
- if b <= beta, then NNA is considered satisfied and the status of n changes from initial to final.
op represent a logic operator and can assume one of the following values: - equality: “==” - less than: “<” - greater than: “>” - equal or less than: “<=” - equal or greater than: “>=” - not equal to: “!=” - within: “IN”
Moreover, NNA allows to specify a triggering status in order to restrain the compartment evaluation to those nodes that:
- match the rule initial state, and
- have at least one neighbors in the triggering status.
Name | Value Type | Default | Mandatory | Description |
---|---|---|---|---|
attribute | string | None | True | Attribute name |
value | numeric(*) | None | True | Attribute testing value |
op | string | None | True | Logic operator |
probability | float in [0, 1] | 1 | False | Event probability |
triggering_status | string | None | False | Trigger |
(*) When op equals “IN” the attribute value is expected to be a tuple of two elements identifying a closed interval.
In the code below is shown the formulation of a model using NodeNumericalAttribute compartments.
The first compartment, c1, is used to implement the transition rule Susceptible->Infected. It restrain the rule evaluation to all those nodes having “Age” equals to 18.
The second compartment, c2, is used to implement the transition rule Infected->Recovered. It restrain the rule evaluation to all those nodes connected at least to a “Susceptible” neighbor and having “Age” in the range [20, 25].
import networkx as nx
import random
import ndlib.models.ModelConfig as mc
import ndlib.models.CompositeModel as gc
import ndlib.models.compartments.NodeNumericalAttribute as na
# Network generation
g = nx.erdos_renyi_graph(1000, 0.1)
# Setting edge attribute
attr = {n: {"Age": random.choice(range(0, 100))} for n in g.nodes()}
nx.set_node_attributes(g, attr)
# Composite Model instantiation
model = gc.CompositeModel(g)
# Model statuses
model.add_status("Susceptible")
model.add_status("Infected")
model.add_status("Removed")
# Compartment definition
c1 = na.NodeNumericalAttribute("Age", value=18, op="==", probability=0.6)
c2 = na.NodeNumericalAttribute("Age", value=[20, 25], op="IN", probability=0.6, triggering_status="Susceptible")
# Rule definition
model.add_rule("Susceptible", "Infected", c1)
model.add_rule("Infected", "Removed", c2)
# Model initial status configuration
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0)
# Simulation execution
model.set_initial_status(config)
iterations = model.iteration_bunch(100)
Node Numerical Variable¶
Node Numerical Variable compartments are used to evaluate events attached to numeric edge attributes or statuses.
Consider the transition rule Addicted->Not addicted that requires that the susceptible node satisfies a specific condition of an internal numeric attribute, attr, to be satisfied (e.g. “Self control” attr < “Craving” status). Such a rule can be described by a simple compartment that models Node Numerical Attribute and Status selection. Let’s call it NNV.
The rule will take as input the initial node status (Susceptible), the final one (Infected) and the NNV compartment. NNV will thus require a probability (beta) of activation.
During each rule evaluation, given a node n and one of its neighbors m
- if the actual status of n equals the rule initial
- if var(n) op var(n) (where var(n) = attr(n) or status(n))
- a random value b in [0,1] will be generated
- if b <= beta, then NNV is considered satisfied and the status of n changes from initial to final.
op represent a logic operator and can assume one of the following values: - equality: “==” - less than: “<” - greater than: “>” - equal or less than: “<=” - equal or greater than: “>=” - not equal to: “!=” - within: “IN”
Moreover, NNV allows to specify a triggering status in order to restrain the compartment evaluation to those nodes that:
- match the rule initial state, and
- have at least one neighbors in the triggering status.
The type of the values that are compared have to be specified in advance, which is done using an enumerated type. This is done to specify whether the first value to be compared is either a status or an attribute, the same thing is done for the second value to be compared. If the value type is not specified, the value to compare the variable to should be a number.
Name | Value Type | Default | Mandatory | Description |
---|---|---|---|---|
variable | string | None | True | The name of the variable to compare |
variable_type | NumericalType | None | True | Numerical type enumerated value |
value | numeric(*)|string | None | True | Name of the testing value or number |
value_type | NumericalType | None | False | Numerical type enumerated value |
op | string | None | True | Logic operator |
probability | float in [0, 1] | 1 | False | Event probability |
triggering_status | string | None | False | Trigger |
(*) When op equals “IN” the attribute value is expected to be a tuple of two elements identifying a closed interval.
In the code below the formulation of a model is shown using NodeNumericalVariable compartments.
The first compartment, condition, is used to implement the transition rule Susceptible->Infected. It restrains the rule evaluation to all those nodes having more “Friends” than 18.
The second compartment, condition2, is used to implement the transition rule Infected->Recovered. It restrains the rule evaluation to all those nodes where “Age” is less than the amount of “Friends” attributes.
Note that instead of attributes, the states could have been used as well by using NumericalType.STATUS instead.
This would only be applicable for numerical states, which can be modelled when using the ContinuousModel
instead of the CompositeModel
.
import networkx as nx
import random
import numpy as np
from ndlib.models.CompositeModel import CompositeModel
from ndlib.models.compartments.NodeStochastic import NodeStochastic
from ndlib.models.compartments.enums.NumericalType import NumericalType
from ndlib.models.compartments.NodeNumericalVariable import NodeNumericalVariable
import ndlib.models.ModelConfig as mc
# Network generation
g = nx.erdos_renyi_graph(1000, 0.1)
# Setting edge attribute
attr = {n: {"Age": random.choice(range(0, 100)), "Friends": random.choice(range(0, 100))} for n in g.nodes()}
nx.set_node_attributes(g, attr)
# Composite Model instantiation
model = CompositeModel(g)
# Model statuses
model.add_status("Susceptible")
model.add_status("Infected")
model.add_status("Removed")
# Compartment definition
condition = NodeNumericalVariable('Friends', var_type=NumericalType.ATTRIBUTE, value=18, op='>')
condition2 = NodeNumericalVariable('Age', var_type=NumericalType.ATTRIBUTE, value='Friends', value_type=NumericalType.ATTRIBUTE, op='<')
# Rule definition
model.add_rule("Susceptible", "Infected", condition)
model.add_rule("Infected", "Removed", condition2)
# Model initial status configuration
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.5)
# Simulation execution
model.set_initial_status(config)
iterations = model.iteration_bunch(100)
Node Threshold¶
Node Threshold compartments are used to evaluate deterministic events attached to network nodes.
Consider the transition rule Susceptible->Infected that requires at least a percentage beta of Infected neighbors for a node n to be satisfied.
Such rule can be described by a simple compartment that models Node Threshold behaviors. Let’s call il NT.
The rule will take as input the initial node status (Susceptible), the final one (Infected) and the NT compartment. NT will thus require a threshold (beta) of activation and a triggering status.
During each rule evaluation, given a node n
- if the actual status of n equals the rule initial one
- let b identify the ratio of n neighbors in the triggering status
- if b >= beta then NS is considered satisfied and the status of n changes from initial to final.
Name | Value Type | Default | Mandatory | Description |
---|---|---|---|---|
threshold | float in [0, 1] | False | Node threshold | |
triggering_status | string | None | True | Trigger |
In the code below is shown the formulation of a Threshold model using NodeThreshold compartments.
The compartment, c1, is used to implement the transition rule Susceptible->Infected. It requires a threshold - here set equals to 0.2.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.CompositeModel as gc
import ndlib.models.compartments.NodeThreshold as ns
# Network generation
g = nx.erdos_renyi_graph(1000, 0.1)
# Composite Model instantiation
model = gc.CompositeModel(g)
# Model statuses
model.add_status("Susceptible")
model.add_status("Infected")
# Compartment definition
c1 = ns.NodeThreshold(0.1, triggering_status="Infected")
# Rule definition
model.add_rule("Susceptible", "Infected", c1)
# Model initial status configuration
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)
# Simulation execution
model.set_initial_status(config)
iterations = model.iteration_bunch(100)
In case of an heterogeneous node threshold distribution the same model can be expressed as follows
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.CompositeModel as gc
import ndlib.models.compartments.NodeThreshold as ns
# Network generation
g = nx.erdos_renyi_graph(1000, 0.1)
# Composite Model instantiation
model = gc.CompositeModel(g)
# Model statuses
model.add_status("Susceptible")
model.add_status("Infected")
# Compartment definition
c1 = ns.NodeThreshold(triggering_status="Infected")
# Rule definition
model.add_rule("Susceptible", "Infected", c1)
# Model initial status configuration
config = mc.Configuration()
# Threshold specs
for i in g.nodes():
config.add_node_configuration("threshold", i, np.random.random_sample())
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)
# Simulation execution
model.set_initial_status(config)
iterations = model.iteration_bunch(100)
Edge Compartments¶
In this class fall all those compartments that evaluate conditions tied to edge features. They model stochastic events as well as deterministic ones.
Edge Stochastic¶
Edge Stochastic compartments are used to evaluate stochastic events attached to network edges.
Consider the transition rule Susceptible->Infected that, to be triggered, requires a direct link among an infected node and a susceptible one. Moreover, it can happens subject to probability beta, a parameter tied to the specific edge connecting the two nodes. Such rule can be described by a simple compartment that models Edge Stochastic behaviors. Let’s call il ES.
The rule will take as input the initial node status (Susceptible), the final one (Infected) and the ES compartment. ES will thus require a probability (beta) of edge activation and a triggering status. In advanced scenarios, where the probability threshold vary from edge to edge, it is possible to specify it using the model configuration object.
During each rule evaluation, given a node n and one of its neighbors m
- if the actual status of n equals the rule initial one and the one of m equals the triggering one
- a random value b in [0,1] will be generated
- if b <= beta then ES is considered satisfied and the status of n changes from initial to final.
Name | Value Type | Default | Mandatory | Description |
---|---|---|---|---|
threshold | float in [0, 1] | 1/N | True | Event probability |
triggering_status | string | None | False | Trigger |
Where N is the number of nodes in the graph.
In the code below is shown the formulation of a Cascade model using EdgeStochastic compartments.
The compartment, c1, is used to implement the transition rule Susceptible->Infected. It requires a probability threshold - here set equals to 0.02 - and restrain the rule evaluation to all those nodes that have at least an Infected neighbors.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.CompositeModel as gc
import ndlib.models.compartments.EdgeStochastic as es
# Network generation
g = nx.erdos_renyi_graph(1000, 0.1)
# Composite Model instantiation
model = gc.CompositeModel(g)
# Model statuses
model.add_status("Susceptible")
model.add_status("Infected")
model.add_status("Removed")
# Compartment definition
c1 = ns.EdgeStochastic(0.02, triggering_status="Infected")
# Rule definition
model.add_rule("Susceptible", "Infected", c1)
# Model initial status configuration
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)
# Simulation execution
model.set_initial_status(config)
iterations = model.iteration_bunch(100)
In case of an heterogeneous edge threshold distribution the same model can be expressed as follows
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.CompositeModel as gc
import ndlib.models.compartments.EdgeStochastic as es
# Network generation
g = nx.erdos_renyi_graph(1000, 0.1)
# Composite Model instantiation
model = gc.CompositeModel(g)
# Model statuses
model.add_status("Susceptible")
model.add_status("Infected")
model.add_status("Removed")
# Compartment definition
c1 = es.EdgeStochastic(triggering_status="Infected")
# Rule definition
model.add_rule("Susceptible", "Infected", c1)
# Model initial status configuration
config = mc.Configuration()
# Threshold specs
for e in g.edges():
config.add_edge_configuration("threshold", e, np.random.random_sample())
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)
# Simulation execution
model.set_initial_status(config)
iterations = model.iteration_bunch(100)
Edge Categorical Attribute¶
Edge Categorical Attribute compartments are used to evaluate events attached to edge attributes.
Consider the transition rule Susceptible->Infected that requires a that the susceptible node is connected to a neighbor through a link expressing a specific value of an internal attribute, attr, to be satisfied (e.g. “type”=”co-worker”). Such rule can be described by a simple compartment that models Edge Categorical Attribute selection. Let’s call il ECA.
The rule will take as input the initial node status (Susceptible), the final one (Infected) and the ECA compartment. ECA will thus require a probability (beta) of activation.
During each rule evaluation, given a node n and one of its neighbors m
- if the actual status of n equals the rule initial
- if attr(n,m) == attr
- a random value b in [0,1] will be generated
- if b <= beta, then ECA is considered satisfied and the status of n changes from initial to final.
Moreover, ECA allows to specify a triggering status in order to restrain the compartment evaluation to those nodes that:
- match the rule initial state, and
- have at least one neighbors in the triggering status.
Name | Value Type | Default | Mandatory | Description |
---|---|---|---|---|
attribute | string | None | True | Attribute name |
value | string | None | True | Attribute testing value |
probability | float in [0, 1] | 1 | False | Event probability |
triggering_status | string | None | False | Trigger |
In the code below is shown the formulation of a model using EdgeCategoricalAttribute compartments.
The first compartment, c1, is used to implement the transition rule Susceptible->Infected. It restrain the rule evaluation to all those nodes connected through a link having the attribute “type” equals “co-worker”.
The second compartment, c2, is used to implement the transition rule Infected->Recovered. It restrain the rule evaluation to all those nodes connected trough a link having the attribute “type” equals “family” whose neighbors is “Susceptible”.
import networkx as nx
import random
import ndlib.models.ModelConfig as mc
import ndlib.models.CompositeModel as gc
import ndlib.models.compartments.EdgeCategoricalAttribute as na
# Network generation
g = nx.erdos_renyi_graph(1000, 0.1)
# Setting edge attribute
attr = {e: {"type": random.choice(['co-worker', 'family'])} for e in g.edges()}
nx.set_edge_attributes(g, attr)
# Composite Model instantiation
model = gc.CompositeModel(g)
# Model statuses
model.add_status("Susceptible")
model.add_status("Infected")
model.add_status("Removed")
# Compartment definition
c1 = na.NodeCategoricalAttribute("type", "co-worker", probability=0.6)
c2 = na.NodeCategoricalAttribute("type", "family", probability=0.6, triggering_status="Susceptible")
# Rule definition
model.add_rule("Susceptible", "Infected", c1)
model.add_rule("Infected", "Recovered", c2)
# Model initial status configuration
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0)
# Simulation execution
model.set_initial_status(config)
iterations = model.iteration_bunch(100)
Edge Numerical Attribute¶
Edge Numerical Attribute compartments are used to evaluate events attached to numeric edge attributes.
Consider the transition rule Susceptible->Infected that requires a that the susceptible node is connected to a neighbor through a link expressing a specific value of an internal numeric attribute, attr, to be satisfied (e.g. “weight”>=3). Such rule can be described by a simple compartment that models Edge Numerical Attribute selection. Let’s call il ENA.
The rule will take as input the initial node status (Susceptible), the final one (Infected) and the ENA compartment. ENA will thus require a probability (beta) of activation.
During each rule evaluation, given a node n and one of its neighbors m
- if the actual status of n equals the rule initial
- if attr(n,m) op attr
- a random value b in [0,1] will be generated
- if b <= beta, then ECA is considered satisfied and the status of n changes from initial to final.
op represent a logic operator and can assume one of the following values: - equality: “==” - less than: “<” - greater than: “>” - equal or less than: “<=” - equal or greater than: “>=” - not equal to: “!=” - within: “IN”
Moreover, ENA allows to specify a triggering status in order to restrain the compartment evaluation to those nodes that:
- match the rule initial state, and
- have at least one neighbors in the triggering status.
Name | Value Type | Default | Mandatory | Description |
---|---|---|---|---|
attribute | string | None | True | Attribute name |
value | numeric(*) | None | True | Attribute testing value |
op | string | None | True | Logic operator |
probability | float in [0, 1] | 1 | False | Event probability |
triggering_status | string | None | False | Trigger |
(*) When op equals “IN” the attribute value is expected to be a tuple of two elements identifying a closed interval.
In the code below is shown the formulation of a model using EdgeNumericalAttribute compartments.
The first compartment, c1, is used to implement the transition rule Susceptible->Infected. It restrain the rule evaluation to all those nodes connected at least to a neighbor through a link having “weight” equals to 4.
The second compartment, c2, is used to implement the transition rule Infected->Recovered. It restrain the rule evaluation to all those nodes connected at least to a “Susceptible” neighbor through a link having “weight” in the range [3, 6].
import networkx as nx
import random
import ndlib.models.ModelConfig as mc
import ndlib.models.CompositeModel as gc
import ndlib.models.compartments.EdgeNumericalAttribute as na
# Network generation
g = nx.erdos_renyi_graph(1000, 0.1)
# Setting edge attribute
attr = {(u, v): {"weight": int((u+v) % 10)} for (u, v) in g.edges()}
nx.set_edge_attributes(g, attr)
# Composite Model instantiation
model = gc.CompositeModel(g)
# Model statuses
model.add_status("Susceptible")
model.add_status("Infected")
model.add_status("Removed")
# Compartment definition
c1 = na.EdgeNumericalAttribute("weight", value=4, op="==", probability=0.6)
c2 = na.EdgeNumericalAttribute("weight", value=(3, 6), op="IN", probability=0.6, triggering_status="Susceptible")
# Rule definition
model.add_rule("Susceptible", "Infected", c1)
model.add_rule("Infected", "Recovered", c2)
# Model initial status configuration
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0)
# Simulation execution
model.set_initial_status(config)
iterations = model.iteration_bunch(100)
Time Compartments¶
In this class fall all those compartments that evaluate conditions tied to temporal execution. They can be used to model, for instance, lagged events as well as triggered transitions.
Count Down¶
Count Down compartments are used to evaluate time related deterministic events attached to network nodes.
Consider the transition rule Susceptible->Infected that has an latent period of t iterations.
Such rule can be described by a simple compartment that models Count Down behaviors. Let’s call il CD.
The rule will take as input the initial node status (Susceptible), the final one (Infected) and the CD compartment. CD will thus require a countdown name (cn) and the number of iterations (t) before activation.
During each rule evaluation, given a node n
- if the actual status of n equals the rule initial one
- if the node does not have an associated countdown cn initialize it to t
- else
- if cn(t) > t decrement cn(t)
- if cn(t) <= t then CD is considered satisfied and the status of n changes from initial to final.
Name | Value Type | Default | Mandatory | Description |
---|---|---|---|---|
name | string | None | True | Count Down name |
iterations | int | None | True | Duration |
In the code below is shown the formulation of a model using CountDown compartments.
The compartment, c1, is used to implement the transition rule Susceptible->Infected. It requires activates after 10 iteration.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.CompositeModel as gc
import ndlib.models.compartments.CountDown as cd
# Network generation
g = nx.erdos_renyi_graph(1000, 0.1)
# Composite Model instantiation
model = gc.CompositeModel(g)
# Model statuses
model.add_status("Susceptible")
model.add_status("Infected")
# Compartment definition
c1 = cd.CountDown("incubation", iterations=10)
# Rule definition
model.add_rule("Susceptible", "Infected", c1)
# Model initial status configuration
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)
# Simulation execution
model.set_initial_status(config)
iterations = model.iteration_bunch(100)
Compartments Composition¶
Compartment can be chained in multiple ways so to describe complex transition rules. In particular, a transition rule can be seen as a tree whose nodes are compartments and edges connections among them.
- The initial node status is evaluated at the root of the tree (the master compartment)
- if the operation described by such compartment is satisfied the conditions of (one of) its child compartments is evaluated
- if a path from the root to one leaf of the tree is completely satisfied the transition rule applies and the node change its status.
Compartments can be combined following two criteria:
Cascading Composition¶
Since each compartment identifies an atomic condition it is natural to imagine rules described as chains of compartments.
A compartment chain identify and ordered set of conditions that needs to be satisfied to allow status transition (it allows describing an AND logic).
To implement such behaviour each compartment exposes a parameter (named composed) that allows to specify the subsequent compartment to evaluate in case it condition is satisfied.
Example¶
In the code below is shown the formulation of a model implementing cascading compartment composition.
The rule Susceptible->Infected is implemented using three NodeStochastic compartments chained as follows:
- If the node n is Susceptible
- c1: if at least a neighbor of the actual node is Infected, with probability 0.5 evaluate compartment c2
- c2: with probability 0.4 evaluate compartment c3
- c3: with probability 0.2 allow the transition to the Infected state
Indeed, heterogeneous compartment types can be mixed to build more complex scenarios.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.CompositeModel as gc
import ndlib.models.compartments as cpm
from ndlib.models.compartments.enums.NumericalType import NumericalType
# Network generation
g = nx.erdos_renyi_graph(1000, 0.1)
# Composite Model instantiation
model = gc.CompositeModel(g)
# Model statuses
model.add_status("Susceptible")
model.add_status("Infected")
# Compartment definition and chain construction
c3 = cpm.NodeStochastic(0.2)
c2 = cpm.NodeStochastic(0.4, composed=c3)
c1 = cpm.NodeStochastic(0.5, "Infected", composed=c2)
# Rule definition
model.add_rule("Susceptible", "Infected", c1)
# Model initial status configuration
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)
# Simulation execution
model.set_initial_status(config)
iterations = model.iteration_bunch(100)
Conditional Composition¶
Since each compartment identifies an atomic condition it is natural to imagine rules described as trees of compartments.
A compartment tree identify and ordered and disjoint set of conditions that needs to be satisfied to allow status transition (it allows describing an OR logic).
To implement such behaviour we implemented a ConditionalComposition compartment that allows to describe branching. Let’s call it CC.
CC evaluate a guard compartment and, depending from the result it gets evaluate (True or False) move to the evaluation of one of its two child compartments.
Parameters¶
Name | Value Type | Default | Mandatory | Description |
---|---|---|---|---|
condition | Compartment | None | True | Guard Compartment |
first_branch | Compartment | None | True | Positive Compartment |
second_branch | Compartment | None | True | Negative Compartment |
Example¶
In the code below is shown the formulation of a model implementing conditional compartment composition.
The rule Susceptible->Infected is implemented using three NodeStochastic compartments chained as follows:
- If the node n is Susceptible
- c1: if at least a neighbor of the actual node is Infected, with probability 0.5 evaluate compartment c2 else evaluate compartment c3
- c2: with probability 0.2 allow the transition to the Infected state
- c3: with probability 0.1 allow the transition to the Infected state
Indeed, heterogeneous compartment types can be mixed to build more complex scenarios.
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.CompositeModel as gc
import ndlib.models.compartments as cpm
from ndlib.models.compartments.enums.NumericalType import NumericalType
import ndlib.models.compartments.ConditionalComposition as cif
# Network generation
g = nx.erdos_renyi_graph(1000, 0.1)
# Composite Model instantiation
model = gc.CompositeModel(g)
# Model statuses
model.add_status("Susceptible")
model.add_status("Infected")
# Compartment definition
c1 = cpm.NodeStochastic(0.5, "Infected")
c2 = cpm.NodeStochastic(0.2)
c3 = cpm.NodeStochastic(0.1)
# Conditional Composition
cc = cif.ConditionalComposition(c1, c2, c3)
# Rule definition
model.add_rule("Susceptible", "Infected", cc)
# Model initial status configuration
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)
# Simulation execution
model.set_initial_status(config)
iterations = model.iteration_bunch(100)
A rule can be defined by employing all possible combinations of cascading and conditional compartment composition.
Examples¶
Here some example of models implemented using compartments.
SIR¶
import networkx as nx
import ndlib.models.ModelConfig as mc
import ndlib.models.CompositeModel as gc
import ndlib.models.compartments.NodeStochastic as ns
# Network generation
g = nx.erdos_renyi_graph(1000, 0.1)
# Composite Model instantiation
model = gc.CompositeModel(g)
# Model statuses
model.add_status("Susceptible")
model.add_status("Infected")
model.add_status("Removed")
# Compartment definition
c1 = ns.NodeStochastic(0.02, triggering_status="Infected")
c2 = ns.NodeStochastic(0.01)
# Rule definition
model.add_rule("Susceptible", "Infected", c1)
model.add_rule("Infected", "Removed", c2)
# Model initial status configuration
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)
# Simulation execution
model.set_initial_status(config)
iterations = model.iteration_bunch(5)
Using continuous states¶
The composite model only supports discrete states, but more advanced custom models might require continuous states and more options. If continuous states are required, it might be better to use the continous model implementation.
Continuous Model¶
Warning
ContinuousModel
requires python3
The composite model only supports discrete states, but more advanced custom models might require continuous states and more options.
The general manner of creating a model remains the same as the CompositeModel
, but it allows for configuration by adding (optional) extra steps.
The general modeling flow is as follows:
- Define a graph
- Add (continuous) internal states
- Define constants and intial values
- Create update conditions
- Add iteration schemes (optional)
- Simulate
- Optional steps(Visualize/Sensitivity analysis)
Graph, internal states and constants¶
The graphs should still be Networkx
graphs, either defined by yourself or generated using one of their built-in functions.
Attributes in the graph can still be accessed and used to update functions.
After a graph is defined, the model can be initialized and internal states can be added to the model. When the model is initalized,
states can be added using add_status(status)
function, where the status argument is a string.
If the model requires certain constant values, these can be added using the constants
parameter when initializing the model.
It should be a dictionary where the key corresponds to the constant name and the value to the constant value.
Adding constants is completely optional.
Example:
import networkx as nx
from ndlib.models.ContinuousModel import ContinuousModel
g = nx.erdos_renyi_graph(n=1000, p=0.1)
constants = {
'constant_1': 0.1,
'constant_2': 2.5
}
model = ContinuousModel(g, constants=constants)
model.add_status('status1')
model.add_status('status2')
Intial values¶
After the graph has been created, the model has been initalized, and the internal states have been added, the next step is to define the intial values of the states.
This is done by creating a dictionary, that maps a state name to an initial value. This value has to be a continous value, that can be statically set, or it can be a function that will be executed for every node. If the value is a function, it should take the following arguments:
- node: the current node for which the initial state is being set
- graph: the full networkx graph containing all nodes
- status: a dictionary that contains all the previously set state (str) -> value (number) mappings for that node
- constants: the dictionary of specified constants, None if no constants are specified
After creating the dictionary, it can be added to the model using the set_initial_status(dict, config)
function.
The config argument is acquired by the Configuration()
function from the ModelConfig
class.
The example below will create a model with 3 states. Every node in the model will initialize status_1 with a value returned by the initial_status_1 function, which will results in all nodes getting a random uniform value between 0 and 0.5. The same happens for the internal state status_2. The third state is constant and thus the same for every node.
import networkx as nx
import numpy as np
from ndlib.models.ContinuousModel import ContinuousModel
import ndlib.models.ModelConfig as mc
def initial_status_1(node, graph, status, constants):
return np.random.uniform(0, 0.5)
def initial_status_2(node, graph, status, constants):
return status['status_1'] + np.random.uniform(0.5, 1)
initial_status = {
'status_1': initial_status_1,
'status_2': initial_status_2,
'status_3': 2
}
g = nx.erdos_renyi_graph(n=1000, p=0.1)
model = ContinuousModel(g)
model.add_status('status_1')
model.add_status('status_2')
model.add_status('status_3')
config = mc.Configuration()
model.set_initial_status(initial_status, config)
Update conditions¶
Another important part of the model is creating conditions and update rules. This follows the same principle as using the compartments, which have already been explained. Every update condition has a:
State: A string matching an internal state
Update function: A function that should be used to update the state if the condition is true. Its arguments are:
- node: The number of the node that is currently updated
- graph: The complete graph containing all the nodes
- status: A status dictionary that maps a node to a dictionary with State -> Value mappings
- attributes: The networkx attributes of the network
- constants: The specified constants, None if not defined
Condition: A compartment condition (Node/Edge/Time)
Scheme (optional): An iteration scheme to specify the iteration number(s) and node(s)
In the example below, the states are updated when the condition NodeStochastic(1)
is true, which is always the case, so the update functions are called every iteration.
Here the state status_1 will be updated every iteration by setting it equal to status_2 + 0.1. The same is done for status_2, but in this case it is set equal to status_1 + 0.5.
import networkx as nx
from ndlib.models.ContinuousModel import ContinuousModel
import ndlib.models.ModelConfig as mc
g = nx.erdos_renyi_graph(n=1000, p=0.1)
model = ContinuousModel(g)
model.add_status('status_1')
model.add_status('status_2')
# Compartments
condition = NodeStochastic(1)
# Update functions
def update_1(node, graph, status, attributes, constants):
return status[node]['status_2'] + 0.1
def update_2(node, graph, status, attributes, constants):
return status[node]['status_1'] + 0.5
# Rules
model.add_rule('status_1', update_1, condition)
model.add_rule('status_2', update_2, condition)
Iteration schemes¶
Another addition to the model, are iteration schemes. These can be used for two things:
- Specify nodes to update
- Specify iteration range when updates should take place
This allows to only update a select amount of nodes during a specific time in the iteration. Under the hood, when schemes are not defined, a default scheme is used for every rule that is active during each iteration and selects all nodes.
To create a scheme, simply define a list where each element is a dictionary containing the following keys:
name: maps to a string that indicates the name of the scheme
function: maps to a function that returns the nodes that should be updated if a condition is true. Its arguments are:
- graph: the full networkx graph
- status: A status dictionary that maps a node to a dictionary with State -> Value mappings
lower (optional): maps to an integer indicating from which iteration the scheme should apply
upper (optional): maps to an integer indicating until which iteration the scheme should apply
After the scheme dictionary is created, it can be added to the model when the model is initalized:
ContinuousModel(graph, constants=constants, iteration_schemes=schemes)
.
Furthermore, if rules are added using the add_rule
function, it should now be done as follows:
model.add_rule('state_name', update_function, condition, scheme_list)
.
Here a rule can be added to multiple schemes. The scheme_list is a list, where every element should match a name of a scheme,
which means that updates can be done in multiple schemes.
If a scheme_list is not provided, the rule will be executed for every iteration, for every node, if the condition is true.
In the example below, the previous model is executed in the same manner,
but this time, the update_1 function is only being evaluated when lower ≤ iteration < upper
,
in this case when the iterations are equal or bigger than 100 but lower than 200.
Furthermore, if the condition is true, the update function is then only executed for the nodes returned by the function specified in the scheme.
In this case a node is selected based on the weighted status_1 value.
Because no scheme has been added to the second rule, it will be evaluated and executed for every node, each iteration.
import networkx as nx
import numpy as np
from ndlib.models.ContinuousModel import ContinuousModel
import ndlib.models.ModelConfig as mc
g = nx.erdos_renyi_graph(n=1000, p=0.1)
# Define schemes
def sample_state_weighted(graph, status):
probs = []
status_1 = [stat['status_1'] for stat in list(status.values())]
factor = 1.0/sum(status_1)
for s in status_1:
probs.append(s * factor)
return np.random.choice(graph.nodes, size=1, replace=False, p=probs)
schemes = [
{
'name': 'random weighted agent',
'function': sample_state_weighted,
'lower': 100,
'upper': 200
}
]
model = ContinuousModel(g, iteration_schemes=schemes)
model.add_status('status_1')
model.add_status('status_2')
# Compartments
condition = NodeStochastic(1)
# Update functions
def update_1(node, graph, status, attributes, constants):
return status[node]['status_2'] + 0.1
def update_2(node, graph, status, attributes, constants):
return status[node]['status_1'] + 0.5
# Rules
model.add_rule('status_1', update_1, condition, ['random weighted agent'])
model.add_rule('status_2', update_2, condition)
Simulation¶
After everything has been specified and added to the model, it can be ran using the iteration_bunch(iterations)
function.
It will run the model iterations amount of times and return the regular output as shown in other models before.
Optional functionalities¶
There are several extra configurations and options:
Often, a model is not only executed once for n amount of iterations. Most of the time meaningful conclusions can be drawn after simulating the model multiple times and even using different inputs.
This is made possible using the ContinuousModelRunner
.
It takes as input a ContinuousModel
object and a Configuration
object (created by using ModelConfig
).
After instantiating the runner object, two functions can be used; one runs the model multiple times for n amount of iterations using different parameters, the other performs sensitivity analysis based on specified measures.
The model can be executed N amount with different parameters using the
run(N, iterations_list, initial_statuses, constants_list=None)
function.
If the length of a list is not equal to the amount of simulations, this is no problem,
as the index of the list will be selected using iteration number % list_length
.
This means if you want to only use one value for every simulation, simply provide a list as argument with only one value.
Run parameters | Value Type | Default | Mandatory | Description |
---|---|---|---|---|
N | number | True | The amount of times to run the simulation | |
iterations_list | list[number] | True | A list containing the amount of iterations to use per simulation | |
initial_statuses | list[dictionary] | True | A list containing initial_status dictionaries to use per simulation | |
constants_list | list[dictionary] | None | False | A list containing constants dictionaries to use per simulation |
Example:
import networkx as nx
import numpy as np
from ndlib.models.ContinuousModel import ContinuousModel
from ndlib.models.ContinuousModelRunner import ContinuousModelRunner
from ndlib.models.compartments.NodeStochastic import NodeStochastic
import ndlib.models.ModelConfig as mc
g = nx.erdos_renyi_graph(n=1000, p=0.1)
def initial_status_1(node, graph, status, constants):
return np.random.uniform(0, 0.5)
def initial_status_2(node, graph, status, constants):
return status['status_1'] + np.random.uniform(0.5, 1)
initial_status = {
'status_1': initial_status_1,
'status_2': initial_status_2,
}
model = ContinuousModel(g)
model.add_status('status_1')
model.add_status('status_2')
# Compartments
condition = NodeStochastic(1)
# Update functions
def update_1(node, graph, status, attributes, constants):
return status[node]['status_2'] + 0.1
def update_2(node, graph, status, attributes, constants):
return status[node]['status_1'] + 0.5
# Rules
model.add_rule('status_1', update_1, condition)
model.add_rule('status_2', update_2, condition)
config = mc.Configuration()
model.set_initial_status(initial_status, config)
# Simulation
runner = ContinuousModelRunner(model, config)
# Simulate the model 10 times with 100 iterations
results = runner.run(10, [100], [initial_status])
Another important part of analysing a model is sensitivity analysis.
Custom analysis can be done using the run function, but an integrated SALib version is included
and can be ran using the analyze_sensitivity(sa_type, initial_status, bounds, n, iterations, second_order=True)
function.
It requires the following parameters:
parameters | Value Type | Default | Mandatory | Description |
---|---|---|---|---|
sa_type | SAType | True | SAType enumerated value indicating what metric to use for sensitivity analysis | |
initial_status | dictionary | True | A dictionary containing the initial status per state | |
bounds | dictionary{status => (lower, upper) | True | A dictionary mapping a status string to a tuple in the form of [lower, upper] | |
n | integer | True | The amount of samples to get from the SALib saltelli sampler | |
iterations | integer | True | A list containing constants dictionaries to use per simulation | |
second_order | boolean | True | False | Boolean indicating whether to include second order indices |
At the moment, after every simulation, the mean value for a state is taken over all the nodes, which is seen as one output for the model.
After running the analysis, a dictionary is returned, mapping a state to a dictionary with the keys “S1”, “S2”, “ST”, “S1_conf”, “S2_conf”, and “ST_conf”
which is acquired by using sobol.analyze()
from SALib.
Note
Currently, the following sensitivity analysis metrics can be passed for the sa_type parameter (use the SAType enum):
- SAType.MEAN
Example:
import networkx as nx
import numpy as np
from ndlib.models.ContinuousModel import ContinuousModel
from ndlib.models.ContinuousModelRunner import ContinuousModelRunner
from ndlib.models.compartments.NodeStochastic import NodeStochastic
from ndlib.models.compartments.enums.SAType import SAType
import ndlib.models.ModelConfig as mc
g = nx.erdos_renyi_graph(n=1000, p=0.1)
constants = {
'constant_1': 0.5,
'constant_2': 0.8
}
def initial_status_1(node, graph, status, constants):
return np.random.uniform(0, 0.5)
def initial_status_2(node, graph, status, constants):
return status['status_1'] + np.random.uniform(0.5, 1)
initial_status = {
'status_1': initial_status_1,
'status_2': initial_status_2,
}
model = ContinuousModel(g, constants=constants)
model.add_status('status_1')
model.add_status('status_2')
# Compartments
condition = NodeStochastic(1)
# Update functions
def update_1(node, graph, status, attributes, constants):
return status[node]['status_2'] * constants['constant_1']
def update_2(node, graph, status, attributes, constants):
return status[node]['status_1'] + constants['constant_2']
# Rules
model.add_rule('status_1', update_1, condition)
model.add_rule('status_2', update_2, condition)
config = mc.Configuration()
model.set_initial_status(initial_status, config)
# Simulation
runner = ContinuousModelRunner(model, config)
analysis = runner.analyze_sensitivity(SAType.MEAN, initial_status, {'constant_1': (0, 1), 'constant_2': (-1, 1)}, 100, 50)
Visualization is often a very important section. The continuous model class allows for a lot of flexibility for what should be shown.
In general, the usual functions like creatings trends, and normally plotting work in the same manner as for the CompositeModel
.
But more features have been added to also visualize the specified network and color the nodes using an internal state.
The other states are shown beneath the network graph using histogram figures.
This is an example generated visualization. It shows addiction as a node color, while the other states of the model are shown using histograms.

To configure and enable visualization, a configuration dictionary should be created first.
It should hold the following key -> value mappings:
Key (string) | Value Type | Default | Mandatory | Description |
---|---|---|---|---|
plot_interval | number | True | How many iterations should be between each plot | |
plot_variable | string | True | The state to use as node color | |
show_plot | boolean | True | False | Whether a plot should be shown |
plot_output | string | False | Should be a path + file name, if set it will save a gif there | |
plot_title | string | Network simulation of plot_variable | False | The title of the visualization |
plot_annotation | string | False | The annotation of the visualization | |
cmin | number | 0 | False | The minimum color to display in the colorbar |
cmax | number | 0 | False | The maximum color to display in the colorbar |
color_scale | string | RdBu | False | Matplotlib colorscale colors to use |
layout | string|function | nx.drawing.spring_layout | False | Name of the networkx layout to use |
layout_params | dictionary | False | Arguments to pass to layout function, takes argument name as key | |
variable_limits | dictionary | {state: [-1, 1] for state in states} | False | Dictionary mapping state name to a list with min and max value |
animation_interval | integer | 30 | False | Amount of miliseconds between each frame |
When the configuration dictionary has been initialized and the model has been initialized, it can be added to the model using the function configure_visualization(visualization_dictionary)
.
Note
By default, if the nodes in the networkx graph already have positions (the pos attribute is set per node), then the positions of the nodes will be used as a layout. If no positions are set, a spring layout will be used, or a specified layout will be used.
The layout
key currently supports some igraph layouts as well, but it requires the igraph and pyintergraph libraries installed.
The following igraph layouts are supported:
fr
: Creates an igraph layout using the fruchterman reingold algorithm
It is possible to include any function, that takes the graph as argument and returns a dictionary of positions keyed by node, just like how the networkx.drawing._layout functions work. This means all networkx layout functions can be included as layout value.
If you wish to pass any specific arguments to the function included as layout, this can be done using the layout_params key. Simply map it to a dict that has the parameter name as key and the desired value as value.
Example:
import networkx as nx
from ndlib.models.ContinuousModel import ContinuousModel
g = nx.erdos_renyi_graph(n=1000, p=0.1)
model = ContinuousModel(g)
model.add_status('status_1')
# Visualization config
visualization_config = {
'plot_interval': 5,
'plot_variable': 'status_1',
'variable_limits': {
'status_1': [0, 0.8]
},
'show_plot': True,
'plot_output': './animations/model_animation.gif',
'plot_title': 'Animated network',
}
model.configure_visualization(visualization_config)
After running the model using the iteration_bunch
function, the returned value can then be used to call the visualize(iterations)
function, which will produce the plot shown in animation above.
It is also possible to recreate the standard static plots using the plot(trends, len(iterations), delta=True)
function. The first argument takes the trends created by the build_trends(iterations)
function.
The ContinuousModelRunner
can be used to simulate a model mutliple times using different parameters.
It also includes sensitivity analysis functionalities.
The visualization section explains how visualizations can be configured, shown, and saved.
Continuous examples¶
Two examples have been added that reproduce models shown in two different papers.
This is an example of how a continuous model that uses multiple internal states can be modelled. In this case, we have modelled the The Dynamics of Addiction: Craving versus Self-Control (Johan Grasman, Raoul P P P Grasman, Han L J van der Maas). The model tries to model addiction by defining several interacting states; craving, self control, addiction, lambda, external influences, vulnerability, and addiction.
It was slightly changed by using the average neighbour addiction to change the External influence variable to make it spread through the network.
import networkx as nx
import random
import numpy as np
import matplotlib.pyplot as plt
from ndlib.models.ContinuousModel import ContinuousModel
from ndlib.models.compartments.NodeStochastic import NodeStochastic
import ndlib.models.ModelConfig as mc
################### MODEL SPECIFICATIONS ###################
constants = {
'q': 0.8,
'b': 0.5,
'd': 0.2,
'h': 0.2,
'k': 0.25,
'S+': 0.5,
}
constants['p'] = 2*constants['d']
def initial_v(node, graph, status, constants):
return min(1, max(0, status['C']-status['S']-status['E']))
def initial_a(node, graph, status, constants):
return constants['q'] * status['V'] + (np.random.poisson(status['lambda'])/7)
initial_status = {
'C': 0,
'S': constants['S+'],
'E': 1,
'V': initial_v,
'lambda': 0.5,
'A': initial_a
}
def update_C(node, graph, status, attributes, constants):
return status[node]['C'] + constants['b'] * status[node]['A'] * min(1, 1-status[node]['C']) - constants['d'] * status[node]['C']
def update_S(node, graph, status, attributes, constants):
return status[node]['S'] + constants['p'] * max(0, constants['S+'] - status[node]['S']) - constants['h'] * status[node]['C'] - constants['k'] * status[node]['A']
def update_E(node, graph, status, attributes, constants):
# return status[node]['E'] - 0.015 # Grasman calculation
avg_neighbor_addiction = 0
for n in graph.neighbors(node):
avg_neighbor_addiction += status[n]['A']
return max(-1.5, status[node]['E'] - avg_neighbor_addiction / 50) # Custom calculation
def update_V(node, graph, status, attributes, constants):
return min(1, max(0, status[node]['C']-status[node]['S']-status[node]['E']))
def update_lambda(node, graph, status, attributes, constants):
return status[node]['lambda'] + 0.01
def update_A(node, graph, status, attributes, constants):
return constants['q'] * status[node]['V'] + min((np.random.poisson(status[node]['lambda'])/7), constants['q']*(1 - status[node]['V']))
################### MODEL CONFIGURATION ###################
# Network definition
g = nx.random_geometric_graph(200, 0.125)
# Visualization config
visualization_config = {
'plot_interval': 2,
'plot_variable': 'A',
'variable_limits': {
'A': [0, 0.8],
'lambda': [0.5, 1.5]
},
'show_plot': True,
'plot_output': './c_vs_s.gif',
'plot_title': 'Self control vs craving simulation',
}
# Model definition
craving_control_model = ContinuousModel(g, constants=constants)
craving_control_model.add_status('C')
craving_control_model.add_status('S')
craving_control_model.add_status('E')
craving_control_model.add_status('V')
craving_control_model.add_status('lambda')
craving_control_model.add_status('A')
# Compartments
condition = NodeStochastic(1)
# Rules
craving_control_model.add_rule('C', update_C, condition)
craving_control_model.add_rule('S', update_S, condition)
craving_control_model.add_rule('E', update_E, condition)
craving_control_model.add_rule('V', update_V, condition)
craving_control_model.add_rule('lambda', update_lambda, condition)
craving_control_model.add_rule('A', update_A, condition)
# Configuration
config = mc.Configuration()
craving_control_model.set_initial_status(initial_status, config)
craving_control_model.configure_visualization(visualization_config)
################### SIMULATION ###################
# Simulation
iterations = craving_control_model.iteration_bunch(100, node_status=True)
trends = craving_control_model.build_trends(iterations)
################### VISUALIZATION ###################
# Show the trends of the model
craving_control_model.plot(trends, len(iterations), delta=True)
# Recreate the plots shown in the paper to verify the implementation
x = np.arange(0, len(iterations))
plt.figure()
plt.subplot(221)
plt.plot(x, trends['means']['E'], label='E')
plt.plot(x, trends['means']['lambda'], label='lambda')
plt.legend()
plt.subplot(222)
plt.plot(x, trends['means']['A'], label='A')
plt.plot(x, trends['means']['C'], label='C')
plt.legend()
plt.subplot(223)
plt.plot(x, trends['means']['S'], label='S')
plt.plot(x, trends['means']['V'], label='V')
plt.legend()
plt.show()
# Show animated plot
craving_control_model.visualize(iterations)
After simulating the model, we get three outputs, the first figure shows the trends of the model per state. It shows the average value per state per iteration and it shows the mean change per state per iteration.
The second figure was created to compare it with a figure that is shown in the paper as verification.
The last figure is an animation that is outputted when the visualize function is called.



This example will be slightly more complex, as it involves different schemes and update functions to model the spread of opinion polarization within and across individuals.
import networkx as nx
import random
import numpy as np
import matplotlib.pyplot as plt
from ndlib.models.ContinuousModel import ContinuousModel
from ndlib.models.compartments.NodeStochastic import NodeStochastic
import ndlib.models.ModelConfig as mc
################### MODEL SPECIFICATIONS ###################
constants = {
'dt': 0.01,
'A_min': -0.5,
'A_star': 1,
's_O': 0.01,
's_I': 0,
'd_A': 0,
'p': 1,
'r_min': 0,
't_O': np.inf,
}
def initial_I(node, graph, status, constants):
return np.random.normal(0, 0.3)
def initial_O(node, graph, status, constants):
return np.random.normal(0, 0.2)
initial_status = {
'I': initial_I,
'O': initial_O,
'A': 1
}
def update_I(node, graph, status, attributes, constants):
nb = np.random.choice(graph.neighbors(node))
if abs(status[node]['O'] - status[nb]['O']) > constants['t_O']:
return status[node]['I'] # Do nothing
else:
# Update information
r = constants['r_min'] + (1 - constants['r_min']) / (1 + np.exp(-1 * constants['p'] * (status[node]['A'] - status[nb]['A'])))
inf = r * status[node]['I'] + (1-r) * status[nb]['I'] + np.random.normal(0, constants['s_I'])
# Update attention
status[node]['A'] = status[node]['A'] + constants['d_A'] * (2 * constants['A_star'] - status[node]['A'])
status[nb]['A'] = status[nb]['A'] + constants['d_A'] * (2 * constants['A_star'] - status[nb]['A'])
return inf
return
def update_A(node, graph, status, attributes, constants):
return status[node]['A'] - 2 * constants['d_A'] * status[node]['A']/len(graph.nodes)
def update_O(node, graph, status, attributes, constants):
noise = np.random.normal(0, constants['s_O'])
x = status[node]['O'] - constants['dt'] * (status[node]['O']**3 - (status[node]['A'] + constants['A_min']) * status[node]['O'] - status[node]['I']) + noise
return x
def shrink_I(node, graph, status, attributes, constants):
return status[node]['I'] * 0.999
def shrink_A(node, graph, status, attributes, constants):
return status[node]['A'] * 0.999
def sample_attention_weighted(graph, status):
probs = []
A = [stat['A'] for stat in list(status.values())]
factor = 1.0/sum(A)
for a in A:
probs.append(a * factor)
return np.random.choice(graph.nodes, size=1, replace=False, p=probs)
schemes = [
{
'name': 'random agent',
'function': sample_attention_weighted,
},
{
'name': 'all',
'function': lambda graph, status: graph.nodes,
},
{
'name': 'shrink I',
'function': lambda graph, status: graph.nodes,
'lower': 5000
},
{
'name': 'shrink A',
'function': lambda graph, status: graph.nodes,
'lower': 10000
},
]
################### MODEL CONFIGURATION ###################
# Network definition
g = nx.watts_strogatz_graph(400, 2, 0.02)
# Visualization config
visualization_config = {
'layout': 'fr',
'plot_interval': 100,
'plot_variable': 'O',
'variable_limits': {
'A': [0, 1]
},
'cmin': -1,
'cmax': 1,
'color_scale': 'RdBu',
'plot_output': './HIOM.gif',
'plot_title': 'HIERARCHICAL ISING OPINION MODEL',
}
# Model definition
HIOM = ContinuousModel(g, constants=constants, iteration_schemes=schemes)
HIOM.add_status('I')
HIOM.add_status('A')
HIOM.add_status('O')
# Compartments
condition = NodeStochastic(1)
# Rules
HIOM.add_rule('I', update_I, condition, ['random agent'])
HIOM.add_rule('A', update_A, condition, ['all'])
HIOM.add_rule('O', update_O, condition, ['all'])
HIOM.add_rule('I', shrink_I, condition, ['shrink I'])
HIOM.add_rule('A', shrink_A, condition, ['shrink A'])
# Configuration
config = mc.Configuration()
HIOM.set_initial_status(initial_status, config)
HIOM.configure_visualization(visualization_config)
################### SIMULATION ###################
iterations = HIOM.iteration_bunch(15000, node_status=True)
trends = HIOM.build_trends(iterations)
################### VISUALIZATION ###################
HIOM.plot(trends, len(iterations), delta=True)
HIOM.visualize(iterations)
NDQL: Network Diffusion Query Language¶
NDlib
aims to an heterogeneous audience composed by technicians as well as analysts.
In order to abstract from the its programming interface we designed a query language to describe diffusion simulations, NDQL
.
Rationale¶
NDQL
is built upon the custom model definition facilities offered by NDlib
.
It provides a simple, declarative, syntax for describing and executing diffusion simulations by
- creating a custom model composed of
- node statuses;
- transition rules (expressed as combinations of
compartments
)
- creating a synthetic graph / loading an existing network
- initialize initial nodes statuses
- run the simulation
NDQL
is designed to allow those users that are not familiar to the Python language to:
- abstract the technicality of the programming interface, and
- directly describe the expected model behaviour
So far, NDQL
supports only static network analysis.
NDQL Syntax¶
An NDQL
script is composed of a minimum set of directives:
- Model definition:
- MODEL, STATUS, COMPARTMENT (+), IF-THEN-ELSE (+), RULE,
- Model initialization:
- INITIALIZE
- Network specification:
- CREATE_NETWORK ($), LOAD_NETWORK ($)
- Simulation execution:
- EXECUTE
Directives marked with (+) are optional while the ones marked with ($) are mutually exclusive w.r.t. their class.
The complete language directive specification is the following:
MODEL model_name
STATUS status_name
COMPARTMENT compartment_name
TYPE compartment_type
COMPOSE compartment_name
[PARAM param_name numeric]*
[TRIGGER status_name]
IF compartment_name_1 THEN compartment_name_2 ELSE compartment_name_3 AS rule_name
RULE rule_name
FROM status_name
TO status_name
USING compartment_name
INITIALIZE
[SET status_name ratio]+
CREATE_NETWORK network_name
TYPE network_type
[PARAM param_name numeric]*
LOAD_NETWORK network_name FROM network_file
EXECUTE model_name ON network_name FOR iterations
The CREATE_NETWORK directive can take as network_type any networkx
graph generator name (param_name are inherited from generator function parameters).
Execute/Translate NDQL files¶
NDlib
installs two command line commands:
- NDQL_translate
- NDQL_execute
The former command allows to translate a generic, well-formed, NDQL
script into an equivalent Python one. It can be executed as
NDQL_translate query_file python_file
where query_file identifies the target NDQL
script and python_file specifies the desired name for the resulting Python script.
The latter command allows to directly execute a generic, well-formed, NDQL
script.It can be executed as
NDQL_execute query_file result_file
where query_file identifies the target NDQL
script and result_file specifies the desired name for the execution results.
Execution results are saved as JSON files with the following syntax:
[{"trends":
{
"node_count": {"0": [270, 179, 15, 0, 0], "1": [30, 116, 273, 256, 239], "2": [0, 5, 12, 44, 61]},
"status_delta": {"0": [0, -91, -164, -15, 0], "1": [0, 86, 157, -17, -17], "2": [0, 5, 7, 32, 17]}
},
"Statuses": {"1": "Infected", "2": "Removed", "0": "Susceptible"}
}]
where - node_count describe the trends built on the number of nodes per status - status_delta describe the trends built on the fluctuations of number of nodes per status - Statuses provides a map from numerical id to status name
Examples¶
Here some example of models implemented using NDQL
.
SIR¶
CREATE_NETWORK g1
TYPE erdos_renyi_graph
PARAM n 300
PARAM p 0.1
MODEL SIR
STATUS Susceptible
STATUS Infected
STATUS Removed
# Compartment definitions
COMPARTMENT c1
TYPE NodeStochastic
PARAM rate 0.1
TRIGGER Infected
COMPARTMENT c2
TYPE NodeStochastic
PARAM rate 0.1
# Rule definitions
RULE
FROM Susceptible
TO Infected
USING c1
RULE
FROM Infected
TO Removed
USING c2
# Model configuration
INITIALIZE
SET Infected 0.1
EXECUTE SIR ON g1 FOR 5
Experiment Server¶
The simulation facilities offered by NDlib
are specifically designed for those users that want to run experiments on their local machine.
However, in some scenarios, e.g. due to limited computational resources or to the rising of other particular needs, it may be convenient to separate the machine on which the definition of the experiment is made from the one that actually executes the simulation.
In order to satisfy such needs, we developed a RESTfull service, NDlib-REST
, that builds upon NDlib
an experiment server queryable through API calls.
Project Website: https://github.com/GiulioRossetti/ndlib-rest
Rationale¶
The simulation web service is designed around the concept of experiment.
An experiment, identified by a unique identifier, is composed of two entities:
- a network, and
- one (or more) configured diffusion models.
Experiments are used to keep track of the simulation definition, to return consecutive model iterations to the user and to store - locally on the experiment server - the current status of the diffusion process.
In particular, in order to perform an experiment, a user must:
- Request a token, which univocally identifies the experiment;
- Select or load a network resource;
- Select one, or more, diffusion model(s);
- (optional) Use the advanced configuration facilities to define node/edge parameters;
- Execute the step-by-step simulation;
- (optional) Reset the experiment status, modify the models/network;
- Destroy the experiment.
The last action, involving the destruction of the experiment, is designed to clean the serialization made by the service of the incremental experiment status.
If an experiment is not explicitly destroyed its data is removed, and the associated token invalidated, after a temporal window that can be configured by the service administrator.
NDlib-REST
is shipped as a Docker container image so to make it configuration free and easier to setup.
Moreover, the simulation server is, by default, executed within a Gunicorn instance allowing parallel executions of multiple experiments at the same time.
NDlib-REST
is built using Flask and offers a standard online documentation page that can also be directly used to test the exposed endpoints both configuring and running experiments.
API Interface¶
As a standard for REST services, all the calls made to NDlib-REST
endpoints generate JSON responses.
The APIs of the simulation service are organized in six categories so to provide a logic separation among all the exposed resources.
In particular, in NDlib-REST
are exposed endpoints handling:
- Experiment: endpoints in this category allow to create, destroy, configure, reset and describe experiments;
- Exploratories: endpoints in this category allow to load predefined scenarios (e.g. specific networks/models with explicit initial configuration);
- Resources: endpoints in this category allow to query the system to dynamically discover the endpoints (and their descriptions) defined within the system;
- Networks: endpoints in this category handle a load of network data as well as the generation of synthetic graphs (Barabasi-Albert, Erdos-Renyi, Watts-Strogatz…);
- Models: endpoints in this category expose the
NDlib
models; - Iterators: endpoints in this category expose the step-by-step and iteration bunch facilities needed to run the simulation.
The simulation service allows to attach multiple diffusion models to the same experiment, thus both the single iteration and the iteration bunch endpoints expose additional parameters that allow the user to select the models for which the call was invoked.
By default, when such parameter is not specified, all the models are executed and their incremental statuses returned.
A particular class of endpoints is the Exploratories one. Such endpoints are used to define the access to pre-set diffusion scenarios. Using such facilities the owner of the simulation server can describe, beforehand, specific scenarios, package them and make them available to the service users.
From an educational point of view such mechanism can be used, for instance, by professors to design emblematic diffusion scenarios (composed by both network and initial node/edge statuses) so to let the students explore their impact on specific models configurations (e.g. to analyze the role of weak-ties and/or community structures).
Installation¶
The project provides:
- The REST service: ndrest.py
- Web API docs: http://127.0.0.1:5000/docs
- Unittest: ndlib-rest/service_test
- Python REST client: ndlib-rest/client
REST service setup¶
Local testing
python ndrest.py
Local testig with multiple workers (using gunicorn web server):
gunicorn -w num_workers -b 127.0.0.1:5000 ndrest:app
In order to change the binding IP/port modify the apidoc.json file. To update the API page run the command:
apidoc -i ndlib-rest/ -o ndlib-rest/static/docs
Docker Container¶
The web application is shipped in a Docker container. You can use the Dockerfile to create a new image and run the web application using the gunicorn application server.
To create the Docker image, install Docker on your machine. To create the image execute the following command from the local copy of the repository
docker build -t [tagname_for_your_image] .
The command create a new image with the specified name. Pay attention to the . a the end of the command.
docker run -d -i -p 5000:5000 [tagname_for_your_image]
This command execute a container with the previous image, bind the local port 5000 to the internal port of the container. The option -d make the container to run in the background (detached)
To have a list of all active container
docker ps -al
To stop a container
docker stop container_name
Configuration¶
In ndrest.py are specified limits for graph sizes.
In particular are set the minimum and maximum numbers of nodes (for both generators and loaded networks) as well as the maximum file sizes for upload.
app.config['MAX_CONTENT_LENGTH'] = 50 * 1024 * 1024 # 50MB limit for uploads
max_number_of_nodes = 100000
min_number_of_nodes = 200 # inherited by networkx
- The “complete graph generator” endpoint represents the only exception to the specified lower bound on number of nodes: such model lowers the minimum to 100 nodes. Indeed, the suggested limits can be increased to handle bigger graphs.
- When loading external graphs nodes MUST be identified by integer ids.
Visual Framework¶
NDlib
aims to an heterogeneous audience composed by technicians as well as analysts.
In order to abstract from the its programming interface we built a simple visual framework that allows to simulate NDlib
built-in models on synthetic graphs.
Project Website: https://github.com/GiulioRossetti/NDLib_viz
Rationale¶
NDlib-Viz
aims to make non-technicians able to design, configure and run epidemic simulations, thus removing the barriers introduced by the usual requirements of programming language knowledge.
Indeed, apart from the usual research-oriented audience, we developed NDlib-Viz
to support students and facilitate teachers to introduce epidemic models.
The platform itself is a web application: it can be executed on a local as well as on a remote NDlib-REST
installation.
Installation¶
NDlib-Viz
requires a local active instance of NDlib-REST
to be executed.
# install dependencies
npm install
# serve with hot reload at localhost:8080
npm run dev
# build for production with minification
npm run build
# build for production and view the bundle analyzer report
npm run build --report
For detailed explanation on how things work, checkout the guide and docs for vue-loader.
Architecture¶
The Visualization Framework is a single-page web application implemented using Javascript and HTML 5. The decoupling of the simulation engine and the visual interface allows us to exploit modern browsers to provide an efficient environment for visualization of models and interactions.
- The structure and layout of the page are managed with Bootstrap.
- The business logic and visualization of graphical widgets are implemented in D3.js.
- Nodes and edges of the networks are drawn using the Force Layout library provided by the D3 library.
- The network visualization is implemented using Canvas object provided by standard HTML5. This allows a very efficient update of the network view.
- The charts showing the Diffusion Trend and Prevalence are created using NVD3 library.
The Visualization Framework is implemented using a Model-Control-View (MCV) design pattern. The model is managed by a central component that implements a REST API client that handle the status of the experiment. When the user interacts with one of the views (charts, network layout, toolbar), the controller notifies the model to update the experiment. Each interaction with the visual interface is managed by the model component that centralizes all the communications with the REST server. The calls to the server are executed asynchronously, and the component updates the visual interface as soon as a response arrives from the server.
Developer Guide¶
Working with NDlib source code¶
Contents:
Introduction¶
These pages describe a git and github workflow for the NDlib project.
There are several different workflows here, for different ways of working with NDlib.
This is not a comprehensive git reference, it’s just a workflow for our own project. It’s tailored to the github hosting service. You may well find better or quicker ways of getting stuff done with git, but these should get you started.
For general resources for learning git, see git resources.
Install git¶
Overview¶
Debian / Ubuntu | sudo apt-get install git |
Fedora | sudo yum install git-core |
Windows | Download and install msysGit [1] |
OS X | Use the git-osx-installer [2] |
In detail¶
See the git page for the most recent information.
Have a look at the github install help pages available from github help [3] .
There are good instructions here: http://book.git-scm.com/2_installing_git.html
[1] | https://git-for-windows.github.io |
[2] | https://code.google.com/archive/p/git-osx-installer/downloads |
[3] | http://help.github.com/ |
Following the latest source¶
These are the instructions if you just want to follow the latest NDlib source, but you don’t need to do any development for now.
The steps are:
- Install git
- get local copy of the ndlib github git repository
- update local copy from time to time
Get the local copy of the code¶
From the command line:
git clone git://github.com/GiulioRossetti/ndlib.git
You now have a copy of the code tree in the new ndlib
directory.
Updating the code¶
From time to time you may want to pull down the latest code. It is necessary to add the NDlib repository as a remote to your configuration file. We call it upstream.
git remote set-url upstream https://github.com/GiulioRossetti/ndlib.git
Now git knows where to fetch updates from.
cd ndlib git fetch upstream
The tree in ndlib
will now have the latest changes from the initial
repository, unless you have made local changes in the meantime. In this case, you have to merge.
git merge upstream/master
It is also possible to update your local fork directly from GitHub:
- Open your fork on GitHub.
- Click on ‘Pull Requests’.
- Click on ‘New Pull Request’. By default, GitHub will compare the original with your fork. If you didn’t make any changes, there is nothing to compare.
- Click on ‘Switching the base’ or click ‘Edit’ and switch the base manually. Now GitHub will compare your fork with the original, and you should see all the latest changes.
- Click on ‘Click to create a pull request for this comparison’ and name your pull request.
- Click on Send pull request.
- Scroll down and click ‘Merge pull request’ and finally ‘Confirm merge’. You will be able to merge it automatically unless you did not change you local repo.
Making a patch¶
You’ve discovered a bug or something else you want to change in ndlib .. - excellent!
You’ve worked out a way to fix it - even better!
You want to tell us about it - best of all!
The easiest way is to make a patch or set of patches. Here we explain how.
Making patches¶
# tell git who you are
git config --global user.email you@yourdomain.example.com
git config --global user.name "Your Name Comes Here"
# get the repository if you don't have it
git clone git://github.com/GiulioRossetti/ndlib.git
# make a branch for your patching
cd networkx
git branch the-fix-im-thinking-of
git checkout the-fix-im-thinking-of
# hack, hack, hack
# Tell git about any new files you've made
git add somewhere/tests/test_my_bug.py
# commit work in progress as you go
git commit -am 'BF - added tests for Funny bug'
# hack hack, hack
git commit -am 'BF - added fix for Funny bug'
# make the patch files
git format-patch -M -C master
Then, open an issue on the projetc GitHub and attach the generated patch files.
Tell git who you are so it can label the commits you’ve made:
git config --global user.email you@yourdomain.example.com git config --global user.name "Your Name Comes Here"
If you don’t already have one, clone a copy of the ndlib repository:
git clone git://github.com/GiulioRossetti/ndlib.git cd networkx
Make a ‘feature branch’. This will be where you work on your bug fix. It’s nice and safe and leaves you with access to an unmodified copy of the code in the main branch:
git branch the-fix-im-thinking-of git checkout the-fix-im-thinking-of
Do some edits, and commit them as you go:
# hack, hack, hack # Tell git about any new files you've made git add somewhere/tests/test_my_bug.py # commit work in progress as you go git commit -am 'BF - added tests for Funny bug' # hack hack, hack git commit -am 'BF - added fix for Funny bug'
Note the
-am
options tocommit
. Them
flag just signals that you’re going to type a message on the command line.When you have finished, check you have committed all your changes:
git status
Finally, make your commits into patches. You want all the commits since you branched from the
master
branch:git format-patch -M -C master
You will now have several files named for the commits:
0001-BF-added-tests-for-Funny-bug.patch 0002-BF-added-fix-for-Funny-bug.patch
Attach these files to a novel issue on the project GitHub.
When you are done, to switch back to the main copy of the
code, just return to the master
branch:
git checkout master
Extend NDlib¶
The NDlib
library can be extended by adding both models and visualization facilities.
In this section are introduced the basilar concept behind the class model adopted in NDlib
and some best practice for the definition of novel models and visualizations.
Describe a Diffusion Model¶
All the diffusion models implemented in NDlib
extends the abstract class ndlib.models.DiffusionModel
.
-
class
ndlib.models.DiffusionModel.
DiffusionModel
(graph, seed=None)¶ Partial Abstract Class that defines Diffusion Models
Such class implements the logic behind model construction, configuration and execution.
In order to describe a novel diffusion algorithm the following steps must be followed:
Model Description¶
As convention a new model should be described in a python file named after it, e.g. a MyModule
class should be implemented in a MyModule.py
file.
-
DiffusionModel.
__init__
(self, graph)¶ Model Constructor
Parameters: graph – A networkx graph object
In oder to effectively describe the model the __init__
function of ndlib.models.DiffusionModel
must be specified as follows:
import future.utils
import numpy as np
import networkx as nx
from ndlib.models.DiffusionModel import DiffusionModel
class MyModel(DiffusionModel):
def __init__(self, graph):
# Call the super class constructor
super(self.__class__, self).__init__(graph)
# Method name
self.name = "MyModel"
# Available node statuses
self.available_statuses = {
"Status_0": 0,
"Status_1": 1
}
# Exposed Parameters
self.parameters = {
"model":
"parameter_name": {
"descr": "Description 1",
"range": [0,1],
"optional": False
},
},
"nodes":
"node_parameter_name": {
"descr": "Description 2",
"range": [0,1],
"optional": True
},
},
"edges":
"edge_parameter_name": {
"descr": "Description 3",
"range": [0,1],
"optional": False
},
},
}
In the __init__
methods three components are used to completely specify the model:
self.name
: its name;self.available_statuses
: the node statuses it allows along with an associated numerical code;self.parameters
: the parameters it requires, their range, description and optionality.
All those information will be used to check the user provided configurations as well as metadata for visualizations.
Iteration Rule¶
Once described the model metadata it is necessary to provide the agent-based description of its general iteration-step.
-
DiffusionModel.
iteration
(self)¶ Execute a single model iteration
Parameters: node_status – if the incremental node status has to be returned. Returns: Iteration_id, (optional) Incremental node status (dictionary node->status), Status count (dictionary status->node count), Status delta (dictionary status->node delta)
To do so, the iteration()
method of the base class has to be overridden in MyModel
as follows:
def iteration(self, node_status=True):
self.clean_initial_status(self.available_statuses.values())
actual_status = {node: nstatus for node, nstatus in self.status.iteritems()}
# if first iteration return the initial node status
if self.actual_iteration == 0:
self.actual_iteration += 1
delta, node_count, status_delta = self.status_delta(actual_status)
if node_status:
return {"iteration": 0, "status": actual_status.copy(),
"node_count": node_count.copy(), "status_delta": status_delta.copy()}
else:
return {"iteration": 0, "status": {},
"node_count": node_count.copy(), "status_delta": status_delta.copy()}
# iteration inner loop
for u in self.graph.nodes():
# evluate possible status changes using the model parameters (accessible via self.params)
# e.g. self.params['beta'], self.param['nodes']['threshold'][u], self.params['edges'][(id_node0, idnode1)]
# identify the changes w.r.t. previous iteration
delta, node_count, status_delta = self.status_delta(actual_status)
# update the actual status and iterative step
self.status = actual_status
self.actual_iteration += 1
# return the actual configuration (only nodes with status updates)
if node_status:
return {"iteration": self.actual_iteration - 1, "status": delta.copy(),
"node_count": node_count.copy(), "status_delta": status_delta.copy()}
else:
return {"iteration": self.actual_iteration - 1, "status": {},
"node_count": node_count.copy(), "status_delta": status_delta.copy()}
The provided template is composed by 4 steps:
- first iteration handling: if present the model returns as result of the first iteration is initial status;
- making a copy of the actual diffusion status;
- iteration loop: definition, and application, of the rules that regulates individual node status transitions;
- construction of the incremental result.
All the steps are mandatory in order to assure a consistent behaviour across different models
All the user specified parameters (models as well as nodes and edges ones) can be used within the iteration()
method: to access them an internal data structure is provided, self.params
.
self.params
is a dictionary that collects all the passed values using the following notation:
- Model parameters:
self.params['model']['parameter_name']
- Node parameters:
self.param['nodes']['nodes_parameter'][node_id]
- Edge parameters:
self.param['edges']['edges_parameter'][(node_id1, node_id2)]
Within the iteration loop the node status updates must be made on the actual_status
data structure, e.g. the copy made during Step 1.
Each iteration returns the incremental status of the diffusion process as well as the iteration progressive number.
Describe a visualization¶
All the matplotlib
visualizations implemented so far in NDlib
extends the abstract class nndlib.viz.mpl.DiffusionViz.DiffusionPlot
.
-
class
ndlib.viz.mpl.DiffusionViz.
DiffusionPlot
(model, trends)¶
Conversely, visualizations that use the bokeh
library, should extend the abstract class nndlib.viz.bokeh.DiffusionViz.DiffusionPlot
.
-
class
ndlib.viz.bokeh.DiffusionViz.
DiffusionPlot
(model, trends)¶
Here is introduced the pattern for describing novel matplotlib
based visualization, bokeh
ones following the same rationale.
So far DiffusionPlot
implements the visualization logic only for generic trend line plot built upon simulation iterations and model metadata.
Line Plot Definition¶
As convention a new visualization should be described in a python file named after it, e.g. a MyViz
class should be implemented in a MyViz.py
file.
-
DiffusionPlot.
__init__
(self, model, iteration)¶ Initialize self. See help(type(self)) for accurate signature.
In oder to effectively describe the visualization the __init__
function of ndlib.viz.bokeh.DiffusionViz.DiffusionPlot
must be specified as follows:
from ndlib.viz.mpl.DiffusionViz import DiffusionPlot
class MyViz(DiffusionPlot):
def __init__(self, model, trends):
super(self.__class__, self).__init__(model, trends)
self.ylabel = "#Nodes"
self.title = "Diffusion Trend"
Data Preparation¶
Once described the plot metadata it is necessary to prepare the data to be visualized through the plot()
method.
To do so, the iteration_series(percentile)
method of the base class has to be overridden in MyViz
.
-
DiffusionPlot.
iteration_series
(self, percentile)¶ Prepare the data to be visualized
Parameters: percentile – The percentile for the trend variance area Returns: a dictionary where iteration ids are keys and the associated values are the computed measures
Such method can access the trend data, as returned by ndlib.models.DiffusionModel.DiffusionModel.build_trends(self, iterations)
in self.iterations
.
Bibliography¶
NDlib
was developed for research purposes.
So far it has been used/cited by the following publications:
- “NDlib: a Python Library to Model and Analyze Diffusion Processes Over Complex Networks”
- G. Rossetti, L. Milli, S. Rinzivillo, A. Sirbu, D. Pedreschi, F. Giannotti. International Journal of Data Science and Analytics. 2017. DOI:0.1007/s41060-017-0086-6 (pre-print available on arXiv)
- “NDlib: Studying Network Diffusion Dynamics”
- G. Rossetti, L. Milli, S. Rinzivillo, A. Sirbu, D. Pedreschi, F. Giannotti. IEEE International Conference on Data Science and Advanced Analytics, DSAA. 2017.
- “Information Diffusion in Complex Networks: The Active/Passive Conundrum”
- L. Milli, G. Rossetti, D. Pedreschi, F. Giannotti International Conference on Complex Networks and their Applications, 2017. DOI:10.1007/978-3-319-72150-7_25
- “Active and passive diffusion processes in complex networks.”
- Milli, L., Rossetti, G., Pedreschi, D., & Giannotti, F. Applied network science, 3(1), 42, 2018.
- “Diffusive Phenomena in Dynamic Networks: a data-driven study”
- L. Milli, G. Rossetti, D. Pedreschi, F. Giannotti. 9th Conference on Complex Networks, CompleNet, 2018.
- “Stochastic dynamic programming heuristics for influence maximization–revenue optimization.”
- Lawrence, Trisha, and Patrick Hosein. International Journal of Data Science and Analytics (2018): 1-14.
- “Optimization of the Choice of Individuals to Be Immunized Through the Genetic Algorithm in the SIR Model”
- Rodrigues, R. F., da Silva, A. R., da Fonseca Vieira, V., AND Xavier, C. R. In International Conference on Computational Science and Its Applications (pp. 62-75), 2018.
- “Algorithmic bias amplifies opinion fragmentation and polarization: A bounded confidence model”
- Sîrbu, A., Pedreschi, D., Giannotti, F., & Kertész, J. PloS one, 14(3), 2019.
- “Similarity forces and recurrent components in human face-to-face interaction networks.”
- Flores, Marco Antonio Rodríguez, and Fragkiskos Papadopoulos. Physical review letters 121.25, 2018.
- “Resampling-based predictive simulation framework of stochastic diffusion model for identifying top-K influential nodes.”
- Ohara, K., Saito, K., Kimura, M., & Motoda, H. International Journal of Data Science and Analytics, 1-21, 2019.
- “Learning Data Mining.”
- Guidotti, R., Monreale, A., & Rinzivillo, S. (2018, October). In IEEE 5th International Conference on Data Science and Advanced Analytics (DSAA) (pp. 361-370), 2018.