Skip to content

Commit

Permalink
Merge pull request #22 from GiulioRossetti/composite
Browse files Browse the repository at this point in the history
NDlib 4.0 (Dengue)
  • Loading branch information
GiulioRossetti authored Jan 25, 2018
2 parents 26ba3a1 + c8823b1 commit 8a24921
Show file tree
Hide file tree
Showing 98 changed files with 3,550 additions and 342 deletions.
211 changes: 7 additions & 204 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ 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](https://doi.org/10.1007/s41060-017-0086-6)
> [DOI:0.1007/s41060-017-0086-6](https://doi.org/10.1007/s41060-017-0086-6) (pre-print available on [arXiv](https://arxiv.org/abs/1801.05854))
> G. Rossetti, L. Milli, S. Rinzivillo, A. Sirbu, D. Pedreschi, F. Giannotti.
> "**NDlib: Studying Network Diffusion Dynamics**",
Expand All @@ -38,215 +38,18 @@ If you use ``NDlib`` as support to your research consider citing:
## Installation

In order to install the library just download (or clone) the current project and copy the ndlib folder in the root of your application.

Alternatively use pip:
```bash
sudo pip install ndlib
```

## Example usage

Generate/load a graph with the [networkx](https://networkx.github.io/) library
```python
import networkx as nx
g = nx.erdos_renyi_graph(1000, 0.1)
```

Import and initialize the selected diffusion model
```python
import ndlib.models.VoterModel as m
model = m.VoterModel(g)
```

Configure model initial status
```python
import ndlib.models.ModelConfig as mc
config = mc.Configuration()
config.add_model_parameter('percentage_infected', 0.2)
model.set_initial_status(config)
```

Execute an iteration of the simulation
```python
it_id, it_status = model.iteration()
```

Execute a bunch of iterations
```python
it_bunch = model.iteration_bunch(bunch_size=10)
```
## Documentation

Each model defines its own node statuses: to retrieve the map used by a given model to identify the available use
```python
model.get_status_map()
```

## Model Configuration Object

Model configuration are defined by a ```ndlib.models.ModelConfig``` object that handle four categories of parameters:
- **model** parameters: ```add_model_parameter(name, value)```
- **node** parameters: ```add_node_configuration(param_name, node_id, param_value)```
- **edge** parameters: ```add_edge_configuration(param_name, edge, param_value)```
- simulation initial **status**: ```add_model_initial_configuration(status_name, node_list)```

We identify a parameter of a given categories as ```category:parameter_name```.

The the complete list of parameters needed by a model are retrievable through
```python
model.get_model_parameters()
```
For examples, tutorials and an complete reference visit the project documentation website on [ReadTheDocs](http://ndlib.readthedocs.io).

Moreover, if the initial set of *Infected* nodes is not given it is mandatory to specify the percentage of infected through ```add_model_parameter("percentage_infected", p)```.
Doing so *p%* of randomly chosen nodes will be selected as the diffusion seeds.


## Visualize simulation Results

NDlib comes with basic visualization facilities embedded in ```ndlib.viz.bokeh.DiffusionTrend``` and ```ndlib.viz.mpl.DiffusionTrend```.

```python
import networkx as nx
from bokeh.io import show
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics.SIRModel as sir
from ndlib.viz.bokeh.DiffusionTrend import DiffusionTrend

g = nx.erdos_renyi_graph(1000, 0.1)
model = sir.SIRModel(g)
config = mc.Configuration()
config.add_model_parameter('beta', 0.001)
config.add_model_parameter('gamma', 0.01)
config.add_model_parameter("percentage_infected", 0.05)
model.set_initial_status(config)
iterations = model.iteration_bunch(200)
trends = model.build_trends(iterations)
viz = DiffusionTrend(model, trends)
p = viz.plot()
show(p)
```
## Collaborate with us!

In order to visually compare multiple executions it is possible to generate multi plots:

```python
import networkx as nx
from bokeh.io import show
import ndlib.models.ModelConfig as mc
import ndlib.models.epidemics.SIRModel as sir
from ndlib.viz.bokeh.DiffusionPrevalence import DiffusionPrevalence
from ndlib.viz.bokeh.DiffusionTrend import DiffusionTrend
from ndlib.viz.bokeh.MultiPlot import MultiPlot

vm = MultiPlot()

g = nx.erdos_renyi_graph(1000, 0.1)
model = sir.SIRModel(g)
config = mc.Configuration()
config.add_model_parameter('beta', 0.001)
config.add_model_parameter('gamma', 0.01)
config.add_model_parameter("percentage_infected", 0.05)
model.set_initial_status(config)
iterations = model.iteration_bunch(200)
trends = model.build_trends(iterations)

viz = DiffusionTrend(model, trends)
p = viz.plot()
vm.add_plot(p)

viz2 = DiffusionPrevalence(model, trends)
p2 = viz2.plot()
vm.add_plot(p2)

m = vm.plot()
show(m)
```
``NDlib`` is an active project, any contribution is welcome!

Matplotlib based visualizations, offered by ```ndlib.viz.bokeh.DiffusionTrend```, also support out-of-the-box model comparisons.

## Implement new models
Implement additional models is simple since it only requires to define a class that:
- implement the partial abstract ```class ndlib.models.DiffusionModel```;
- redefine the ```__init__()``` method to provide model details;
- implement the ```iteration(node_status)``` method specifying its agent-based rules.

### Structure Example
```python
from ndlib.models.DiffusionModel import DiffusionModel

class MyModel(DiffusionModel):

def __init__(self, graph):
super(self.__class__, self).__init__(graph)
self.available_statuses = {
"Susceptible": 0,
"Infected": 1
}
self.parameters = {
"model": {
"param1": {
"descr": "descr1",
"range": [0, 1],
"optional": False},
"param2": {
"descr": "descr2",
"range": [0, 1],
"optional": True}
},
"nodes": {
"param3": {
"descr": "descr3",
"range": [0, 1],
"optional": True},
"param4": {
"descr": "descr4",
"range": [0, 1],
"optional": True}
},
"edges": {
"param5": {
"descr": "descr5",
"range": [0, 1],
"optional": False},
"param6": {
"descr": "descr6",
"range": [0, 1],
"optional": True}
},
}
self.name = "MyModel"


def iteration(self, node_status=True):

self.clean_initial_status(self.available_statuses.values())
# 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()}

actual_status = {node: nstatus for node, nstatus in self.status.iteritems()}
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()}

```
If you like to include your model in NDlib (as well as in [NDlib-REST](https://github.com/GiulioRossetti/ndlib-rest)) feel free to fork the project, open an issue and contact us.
If you like to include your model in our library (as well as in [NDlib-REST](https://github.com/GiulioRossetti/ndlib-rest)) feel free to fork the project, open an issue and contact us.
24 changes: 24 additions & 0 deletions docs/bibliography.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,27 @@
Bibliography
************

``NDlib`` was developed for research purposes.

So far it has been used as support to the following publications:

.. highlights::

**"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 <https://dx.doi.org/10.1007/s41060-017-0086-6>`_
(pre-print available on `arXiv <https://arxiv.org/abs/1801.05854>`_)

**"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 <https://dx.doi.org/10.1007/978-3-319-72150-7_25>`_

**"Diffusive Phenomena in Dynamic Networks: a data-driven study"**
L. Milli, G. Rossetti, D. Pedreschi, F. Giannotti.
9th Conference on Complex Networks, CompleNet, 2018.
6 changes: 3 additions & 3 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,17 @@ def __getattr__(cls, name):

# General information about the project.
project = u'NDlib'
copyright = u'2017, Giulio Rossetti'
copyright = u'2018, Giulio Rossetti'
author = u'Giulio Rossetti'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = u'2.0.1'
version = u'4.0.0'
# The full version, including alpha/beta/rc tags.
release = u'2.0.1'
release = u'4.0.0'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
57 changes: 57 additions & 0 deletions docs/custom/compartments/CascadingComposition.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
*********************
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.

.. code-block:: python
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")
# Compartment definition and chain construction
c3 = ns.NodeStochastic(0.2)
c2 = ns.NodeStochastic(0.4, composed=c3)
c1 = ns.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('percentage_infected', 0.1)
# Simulation execution
model.set_initial_status(config)
iterations = model.iteration_bunch(100)
Loading

0 comments on commit 8a24921

Please sign in to comment.