Skip to content

Commit

Permalink
add GeneralThreshold Model
Browse files Browse the repository at this point in the history
  • Loading branch information
letiziam committed Oct 3, 2019
1 parent 8fc907e commit 729c04e
Show file tree
Hide file tree
Showing 4 changed files with 257 additions and 2 deletions.
122 changes: 122 additions & 0 deletions docs/reference/models/epidemics/GeneralThreshold.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
*********************
General Threshold
*********************

The General Threshold model was introduced in 20003 by Kempe [#]_.

In this model, during an epidemics, a 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 if the sum of the weight of the infected neighbors is greater than the threshold

--------
Statuses
--------
During the simulation a node can experience the following statuses:

=========== ====
Name Code
=========== ====
Susceptible 0
Infected 1
=========== ====

----------
Parameters
----------

========= ===== =============== ======= ========= =======================
Name Type Value Type Default Mandatory Description
========= ===== =============== ======= ========= =======================
threshold Node float in [0, 1] 0.1 False Individual threshold
weight Edge float in [0, 1] 0.1 False Edge weight
========= ===== =============== ======= ========= =======================

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.

-------
Methods
-------

The following class methods are made available to configure, describe and execute the simulation:

^^^^^^^^^
Configure
^^^^^^^^^
.. autoclass:: ndlib.models.epidemics.GeneralThresholdModel.GeneralThresholdModel
.. automethod:: ndlib.models.epidemics.GeneralThresholdModel.GeneralThresholdModel.__init__(graph)

.. automethod:: ndlib.models.epidemics.GeneralThresholdModel.GeneralThresholdModel.set_initial_status(self, configuration)
.. automethod:: ndlib.models.epidemics.GeneralThresholdModel.GeneralThresholdModel.reset(self)

^^^^^^^^
Describe
^^^^^^^^

.. automethod:: ndlib.models.epidemics.GeneralThresholdModel.GeneralThresholdModel.get_info(self)
.. automethod:: ndlib.models.epidemics.GeneralThresholdModel.GeneralThresholdModel.get_status_map(self)

^^^^^^^^^^^^^^^^^^
Execute Simulation
^^^^^^^^^^^^^^^^^^
.. automethod:: ndlib.models.epidemics.GeneralThresholdModel.GeneralThresholdModel.iteration(self)
.. automethod:: ndlib.models.epidemics.GeneralThresholdModel.GeneralThresholdModel.iteration_bunch(self, bunch_size)


-------
Example
-------

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.


.. code-block:: python
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 = epd.GeneralThresholdModel(g)
# Model Configuration
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)
# Setting node and edges parameters
threshold = 0.25
weight = 0.2
if isinstance(g, nx.Graph):
nodes = g.nodes
edges = g.edges
else:
nodes = g.vs['name']
edges = [(g.vs[e.tuple[0]]['name'], g.vs[e.tuple[1]]['name']) for e in g.es]
for i in nodes:
config.add_node_configuration("threshold", i, threshold)
for e in edges:
config.add_edge_configuration("weight", e, weight)
model.set_initial_status(config)
# Simulation execution
iterations = model.iteration_bunch(200)
.. [#] János Török and János Kertész “Cascading collapse of online social networks” Scientific reports, vol. 7 no. 1, 2017
David Kempe , Jon Kleinberg, and Éva Tardos. "Maximizing the spread of influence through a social network." Proceedings of the ninth ACM SIGKDD international conference on Knowledge discovery and data mining. ACM, 2003.
102 changes: 102 additions & 0 deletions ndlib/models/epidemics/GeneralThresholdModel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from ..DiffusionModel import DiffusionModel
import future.utils


__author__ = "Letizia Milli"
__license__ = "BSD-2-Clause"
__email__ = "letizia.milli@di.unipi.it"


class GeneralThresholdModel(DiffusionModel):
"""
Node Parameters to be specified via ModelConfig
:param threshold: The node threshold. If not specified otherwise a value of 0.1 is assumed for all nodes.
:param weight: The edge weight. If not specified otherwise a value of 0.1 is assumed for all edges.
"""

def __init__(self, graph):
"""
Model Constructor
:param graph: A networkx graph object
"""
super(self.__class__, self).__init__(graph)
self.available_statuses = {
"Susceptible": 0,
"Infected": 1
}

self.parameters = {
"model": {},
"nodes": {
"threshold": {
"descr": "Node threshold",
"range": [0, 1],
"optional": True,
"default": 0.1
}
},
"edges": {
"weight": {
"descr": "Edge threshold",
"range": [0, 1],
"optional": True,
"default": 0.1
}
},
}

self.name = "GeneralThresholdModel"

def iteration(self, node_status=True):
"""
Execute a single model iteration
:return: Iteration_id, Incremental node status (dictionary node->status)
"""
self.clean_initial_status(self.available_statuses.values())

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

for u in self.graph.nodes:
if actual_status[u] == 1:
continue

neighbors = list(self.graph.neighbors(u))
if self.graph.directed:
neighbors = list(self.graph.predecessors(u))

weight = 0
for v in neighbors:
if self.status[v] == 1:
key = (u, v)
if key in self.params['edges']['weight']:
weight += self.params['edges']['weight'][key]
elif (v, u) in self.params['edges']['weight'] and not self.graph.directed:
weight += self.params['edges']['weight'][(v, u)]

if len(neighbors) > 0:
if weight >= self.params['nodes']['threshold'][u]:
actual_status[u] = 1

delta, node_count, status_delta = self.status_delta(actual_status)
self.status = actual_status
self.actual_iteration += 1

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()}
4 changes: 3 additions & 1 deletion ndlib/models/epidemics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from .ThresholdModel import ThresholdModel
from .ICEModel import ICEModel
from .ICPModel import ICPModel
from .GeneralThresholdModel import GeneralThresholdModel

__all__ = [
'GeneralisedThresholdModel',
Expand All @@ -31,5 +32,6 @@
'SWIRModel',
'ThresholdModel',
'ICEModel',
'ICPModel'
'ICPModel',
'GeneralThresholdModel'
]
31 changes: 30 additions & 1 deletion ndlib/test/test_ndlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,6 @@ def test_threshold_model(self):
self.assertEqual(len(iterations), 10)

def test_generalisedthreshold_model(self):

for g in get_graph(True):
model = epd.GeneralisedThresholdModel(g)
config = mc.Configuration()
Expand All @@ -308,6 +307,36 @@ def test_generalisedthreshold_model(self):
iterations = model.iteration_bunch(50, node_status=False)
self.assertEqual(len(iterations), 50)


def test_GeneralThresholdModel(self):
for g in get_graph(True):
model = epd.GeneralThresholdModel(g)
config = mc.Configuration()
config.add_model_parameter('fraction_infected', 0.1)

threshold = 0.2
weight = 0.2
if isinstance(g, nx.Graph):
nodes = g.nodes
edges = g.edges
else:
nodes = g.vs['name']
edges = [(g.vs[e.tuple[0]]['name'], g.vs[e.tuple[1]]['name']) for e in g.es]


for i in nodes:
config.add_node_configuration("threshold", i, threshold)
for e in edges:
config.add_edge_configuration("weight", e, weight)


model.set_initial_status(config)
iterations = model.iteration_bunch(10)
self.assertEqual(len(iterations), 10)
iterations = model.iteration_bunch(10, node_status=False)
self.assertEqual(len(iterations), 10)


def test_profile_threshold_model(self):
for g in get_graph(True):
model = epd.ProfileThresholdModel(g)
Expand Down

0 comments on commit 729c04e

Please sign in to comment.