Skip to content

Commit e210e53

Browse files
Merge branch 'net_abstraction' into master
2 parents 4676a20 + 47681f1 commit e210e53

29 files changed

+677
-478
lines changed

ndlib/models/CompositeModel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def iteration(self, node_status=True):
4747
return {"iteration": 0, "status": {},
4848
"node_count": node_count.copy(), "status_delta": status_delta.copy()}
4949

50-
for u in self.graph.nodes():
50+
for u in self.graph.nodes:
5151
u_status = self.status[u]
5252
for i in range(0, self.compartment_progressive):
5353

ndlib/models/DiffusionModel.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import past.builtins
55
import future.utils
66
import six
7+
from netdispatch import AGraph
78

89
__author__ = "Giulio Rossetti"
910
__license__ = "BSD-2-Clause"
@@ -52,8 +53,8 @@ def __init__(self, graph):
5253
}
5354

5455
self.actual_iteration = 0
55-
self.graph = graph
56-
self.status = {n: 0 for n in self.graph.nodes()}
56+
self.graph = AGraph(graph)
57+
self.status = {n: 0 for n in self.graph.nodes}
5758
self.initial_status = {}
5859

5960
def __validate_configuration(self, configuration):
@@ -99,13 +100,13 @@ def __validate_configuration(self, configuration):
99100
if len(onp) > 0:
100101
for param in onp:
101102
if param not in ndp:
102-
for nid in self.graph.nodes():
103+
for nid in self.graph.nodes:
103104
configuration.add_node_configuration(param, nid, self.parameters['nodes'][param]['default'])
104105

105106
if len(oep) > 0:
106107
for param in oep:
107108
if param not in edp:
108-
for eid in self.graph.edges():
109+
for eid in self.graph.edges:
109110
configuration.add_edge_configuration(param, eid, self.parameters['edges'][param]['default'])
110111

111112
# Checking initial simulation status
@@ -128,15 +129,15 @@ def set_initial_status(self, configuration):
128129
# Set additional node information
129130

130131
for param, node_to_value in future.utils.iteritems(nodes_cfg):
131-
if len(node_to_value) < len(self.graph.nodes()):
132+
if len(node_to_value) < len(self.graph.nodes):
132133
raise ConfigurationException({"message": "Not all nodes have a configuration specified"})
133134

134135
self.params['nodes'][param] = node_to_value
135136

136137
edges_cfg = configuration.get_edges_configuration()
137138
# Set additional edges information
138139
for param, edge_to_values in future.utils.iteritems(edges_cfg):
139-
if len(edge_to_values) == len(self.graph.edges()):
140+
if len(edge_to_values) == len(self.graph.edges):
140141
self.params['edges'][param] = {}
141142
for e in edge_to_values:
142143
self.params['edges'][param][e] = edge_to_values[e]
@@ -227,7 +228,6 @@ def reset(self, infected_nodes=None):
227228
if 'fraction_infected' in self.params['model']:
228229
for n in self.status:
229230
self.status[n] = 0
230-
231231
number_of_initial_infected = len(self.graph.nodes()) * float(self.params['model']['fraction_infected'])
232232
available_nodes = [n for n in self.status if self.status[n] == 0]
233233
sampled_nodes = np.random.choice(available_nodes, int(number_of_initial_infected), replace=False)

ndlib/models/DynamicDiffusionModel.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
import networkx as nx
44
from ndlib.models.DiffusionModel import DiffusionModel
55
import six
6+
import warnings
7+
import numpy as np
8+
import future.utils
69

710
__author__ = "Giulio Rossetti"
811
__license__ = "BSD-2-Clause"
@@ -85,4 +88,116 @@ def execute_iterations(self, node_status=True):
8588
system_status.append(its)
8689
return system_status
8790

91+
def set_initial_status(self, configuration):
92+
"""
93+
Set the initial model configuration
94+
95+
:param configuration: a ```ndlib.models.ModelConfig.Configuration``` object
96+
"""
97+
98+
self.__validate_configuration(configuration)
99+
100+
nodes_cfg = configuration.get_nodes_configuration()
101+
# Set additional node information
102+
103+
for param, node_to_value in future.utils.iteritems(nodes_cfg):
104+
if len(node_to_value) < len(self.graph.nodes()):
105+
raise ConfigurationException({"message": "Not all nodes have a configuration specified"})
106+
107+
self.params['nodes'][param] = node_to_value
108+
109+
edges_cfg = configuration.get_edges_configuration()
110+
# Set additional edges information
111+
for param, edge_to_values in future.utils.iteritems(edges_cfg):
112+
if len(edge_to_values) == len(self.graph.edges()):
113+
self.params['edges'][param] = {}
114+
for e in edge_to_values:
115+
self.params['edges'][param][e] = edge_to_values[e]
116+
117+
# Set initial status
118+
model_status = configuration.get_model_configuration()
119+
120+
for param, nodes in future.utils.iteritems(model_status):
121+
self.params['status'][param] = nodes
122+
for node in nodes:
123+
self.status[node] = self.available_statuses[param]
124+
125+
# Set model additional information
126+
model_params = configuration.get_model_parameters()
127+
for param, val in future.utils.iteritems(model_params):
128+
self.params['model'][param] = val
129+
130+
# Handle initial infection
131+
if 'Infected' not in self.params['status']:
132+
if 'percentage_infected' in self.params['model']:
133+
number_of_initial_infected = len(self.graph.nodes()) * float(self.params['model']['percentage_infected'])
134+
if number_of_initial_infected < 1:
135+
warnings.warn('Graph with less than 100 nodes: a single node will be set as infected')
136+
number_of_initial_infected = 1
137+
138+
available_nodes = [n for n in self.status if self.status[n] == 0]
139+
sampled_nodes = np.random.choice(available_nodes, int(number_of_initial_infected), replace=False)
140+
for k in sampled_nodes:
141+
self.status[k] = self.available_statuses['Infected']
142+
143+
self.initial_status = self.status
144+
145+
def __validate_configuration(self, configuration):
146+
"""
147+
Validate the consistency of a Configuration object for the specific model
148+
149+
:param configuration: a Configuration object instance
150+
"""
151+
if "Infected" not in self.available_statuses:
152+
raise ConfigurationException("'Infected' status not defined.")
153+
154+
# Checking mandatory parameters
155+
omp = set([k for k in self.parameters['model'].keys() if not self.parameters['model'][k]['optional']])
156+
onp = set([k for k in self.parameters['nodes'].keys() if not self.parameters['nodes'][k]['optional']])
157+
oep = set([k for k in self.parameters['edges'].keys() if not self.parameters['edges'][k]['optional']])
158+
159+
mdp = set(configuration.get_model_parameters().keys())
160+
ndp = set(configuration.get_nodes_configuration().keys())
161+
edp = set(configuration.get_edges_configuration().keys())
162+
163+
if len(omp) > 0:
164+
if len(omp & mdp) != len(omp):
165+
raise ConfigurationException({"message": "Missing mandatory model parameter(s)", "parameters": omp-mdp})
166+
167+
if len(onp) > 0:
168+
if len(onp & ndp) != len(onp):
169+
raise ConfigurationException({"message": "Missing mandatory node parameter(s)", "parameters": onp-ndp})
170+
171+
if len(oep) > 0:
172+
if len(oep & edp) != len(oep):
173+
raise ConfigurationException({"message": "Missing mandatory edge parameter(s)", "parameters": oep-edp})
174+
175+
# Checking optional parameters
176+
omp = set([k for k in self.parameters['model'].keys() if self.parameters['model'][k]['optional']])
177+
onp = set([k for k in self.parameters['nodes'].keys() if self.parameters['nodes'][k]['optional']])
178+
oep = set([k for k in self.parameters['edges'].keys() if self.parameters['edges'][k]['optional']])
179+
180+
if len(omp) > 0:
181+
for param in omp:
182+
if param not in mdp:
183+
configuration.add_model_parameter(param, self.parameters['model'][param]['default'])
184+
185+
if len(onp) > 0:
186+
for param in onp:
187+
if param not in ndp:
188+
for nid in self.graph.nodes():
189+
configuration.add_node_configuration(param, nid, self.parameters['nodes'][param]['default'])
190+
191+
if len(oep) > 0:
192+
for param in oep:
193+
if param not in edp:
194+
for eid in self.graph.edges():
195+
configuration.add_edge_configuration(param, eid, self.parameters['edges'][param]['default'])
196+
197+
# Checking initial simulation status
198+
sts = set(configuration.get_model_configuration().keys())
199+
if self.discrete_state and "Infected" not in sts and "percentage_infected" not in mdp:
200+
warnings.warn('Initial infection missing: a random sample of 5% of graph nodes will be set as infected')
201+
self.params['model']["percentage_infected"] = 0.05
202+
88203
iteration_bunch = execute_snapshots

ndlib/models/compartments/EdgeCategoricalAttribute.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def execute(self, node, graph, status, status_map, *args, **kwargs):
2525
if isinstance(graph, nx.DiGraph):
2626
neighbors = list(graph.predecessors(node))
2727

28-
edge_attr = nx.get_edge_attributes(graph, self.attribute)
28+
edge_attr = graph.get_edge_attributes(self.attribute)
2929

3030
if self.trigger is not None:
3131
triggered = [v for v in neighbors if status[v] == status_map[self.trigger] and

ndlib/models/compartments/EdgeNumericalAttribute.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def execute(self, node, graph, status, status_map, *args, **kwargs):
4343
if isinstance(graph, nx.DiGraph):
4444
neighbors = list(graph.predecessors(node))
4545

46-
edge_attr = nx.get_edge_attributes(graph, self.attribute)
46+
edge_attr = graph.get_edge_attributes(self.attribute)
4747

4848
triggered = []
4949

ndlib/models/compartments/EdgeStochastic.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from ndlib.models.compartments.Compartment import Compartiment, ConfigurationException
22
import numpy as np
3-
import networkx as nx
43

54
__author__ = 'Giulio Rossetti'
65
__license__ = "BSD-2-Clause"
@@ -18,7 +17,13 @@ def __init__(self, threshold=None, triggering_status=None, **kwargs):
1817

1918
def execute(self, node, graph, status, status_map, params, *args, **kwargs):
2019
neighbors = list(graph.neighbors(node))
21-
if isinstance(graph, nx.DiGraph):
20+
21+
try:
22+
directed = graph.directed
23+
except AttributeError:
24+
directed = graph.is_directed()
25+
26+
if directed:
2227
neighbors = list(graph.predecessors(node))
2328

2429
threshold = float(1)/len(neighbors)

ndlib/models/compartments/NodeStochastic.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from ndlib.models.compartments.Compartment import Compartiment
22
import numpy as np
3-
import networkx as nx
43

54
__author__ = 'Giulio Rossetti'
65
__license__ = "BSD-2-Clause"
@@ -16,7 +15,12 @@ def __init__(self, rate, triggering_status=None, **kwargs):
1615

1716
def execute(self, node, graph, status, status_map, *args, **kwargs):
1817
neighbors = graph.neighbors(node)
19-
if isinstance(graph, nx.DiGraph):
18+
try:
19+
directed = graph.directed
20+
except AttributeError:
21+
directed = graph.is_directed()
22+
23+
if directed:
2024
neighbors = graph.predecessors(node)
2125

2226
p = np.random.random_sample()

ndlib/models/compartments/NodeThreshold.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from ndlib.models.compartments.Compartment import Compartiment, ConfigurationException
2-
import networkx as nx
32

43
__author__ = 'Giulio Rossetti'
54
__license__ = "BSD-2-Clause"
@@ -16,7 +15,13 @@ def __init__(self, threshold=None, triggering_status=None, **kwargs):
1615
def execute(self, node, graph, status, status_map, params, *args, **kwargs):
1716
neighbors = list(graph.neighbors(node))
1817
test = False
19-
if isinstance(graph, nx.DiGraph):
18+
19+
try:
20+
directed = graph.directed
21+
except AttributeError:
22+
directed = graph.is_directed()
23+
24+
if directed:
2025
neighbors = list(graph.predecessors(node))
2126

2227
if self.trigger is None:

ndlib/models/epidemics/GeneralisedThresholdModel.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from ..DiffusionModel import DiffusionModel
2-
import networkx as nx
32
import future.utils
43
import random
54
import queue
@@ -70,10 +69,10 @@ def iteration(self, node_status=True):
7069
return 0, actual_status
7170

7271
gamma = float(self.params['model']['mu']) * float(self.actual_iteration) / float(self.params['model']['tau'])
73-
list_node = self.graph.nodes()
72+
list_node = self.graph.nodes
7473
start = min(list_node)
7574
stop = max(list_node)
76-
number_node_susceptible = len(self.graph.nodes()) - sum(self.status.values())
75+
number_node_susceptible = len(self.graph.nodes) - sum(self.status.values())
7776

7877
while gamma >= 1 and number_node_susceptible >= 1:
7978
random_index = random.randrange(start, stop+1, 1)
@@ -82,12 +81,12 @@ def iteration(self, node_status=True):
8281
gamma -= 1
8382
number_node_susceptible -= 1
8483

85-
for u in self.graph.nodes():
84+
for u in self.graph.nodes:
8685
if actual_status[u] == 1:
8786
continue
8887

8988
neighbors = list(self.graph.neighbors(u))
90-
if isinstance(self.graph, nx.DiGraph):
89+
if self.graph.directed:
9190
neighbors = list(self.graph.predecessors(u))
9291

9392
infected = 0

ndlib/models/epidemics/IndependentCascadesModel.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from ..DiffusionModel import DiffusionModel
22
import numpy as np
33
import future.utils
4-
import networkx as nx
54

65
__author__ = 'Giulio Rossetti'
76
__license__ = "BSD-2-Clause"
@@ -62,7 +61,7 @@ def iteration(self, node_status=True):
6261
return {"iteration": 0, "status": {},
6362
"node_count": node_count.copy(), "status_delta": status_delta.copy()}
6463

65-
for u in self.graph.nodes():
64+
for u in self.graph.nodes:
6665
if self.status[u] != 1:
6766
continue
6867

@@ -80,7 +79,7 @@ def iteration(self, node_status=True):
8079
if 'threshold' in self.params['edges']:
8180
if key in self.params['edges']['threshold']:
8281
threshold = self.params['edges']['threshold'][key]
83-
elif (v, u) in self.params['edges']['threshold'] and not nx.is_directed(self.graph):
82+
elif (v, u) in self.params['edges']['threshold'] and not self.graph.directed:
8483
threshold = self.params['edges']['threshold'][(v, u)]
8584

8685
flip = np.random.random_sample()

0 commit comments

Comments
 (0)