This repository has been archived by the owner on Nov 11, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSISStochasticDynamicsRewireDegree.py
174 lines (128 loc) · 6.75 KB
/
SISStochasticDynamicsRewireDegree.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# coding: utf-8
# In[4]:
from GraphWithStochasticDynamics import *
# In[5]:
class SISStochasticDynamicsRewireDegree(GraphWithStochasticDynamics):
'''An SIR dynamics with stochastic simulation.'''
# the possible dynamics states of a node for SIR dynamics
SUSCEPTIBLE = 'susceptible'
INFECTED = 'infected'
degree_dict = dict()
# list of SI edges connecting a susceptible to an infected node
_si = []
def __init__( self, time_limit = 10000, p_infect = 0.0, p_recover = 1.0, p_infected = 0.0, graph = None, p_rewire = 0.0 ):
'''Generate a graph with dynamics for the given parameters.
p_infect: infection probability (defaults to 0.0)
p_recover: probability of recovery (defaults to 1.0)
p_infected: initial infection probability (defaults to 0.0)
graph: the graph to copy from (optional)'''
states = {self.SUSCEPTIBLE,self.INFECTED}
rates = dict()
rates['p_infect'] = p_infect
rates['p_recover'] = p_recover
rates['p_infected'] = p_infected
rates['p_rewire'] = p_rewire
GraphWithStochasticDynamics.__init__(self, time_limit = time_limit, graph = graph, states = states, rates = rates)
self.p_infected = p_infected
self.p_infect = p_infect
self.p_recover = p_recover
self.p_rewire = p_rewire
for i in self.nodes():
self.degree_dict[i] = self.degree(i)
def before( self ):
'''Seed the network with infected nodes, extract the initial set of
SI nodes, and mark all edges as unoccupied by the dynamics.'''
self._si = []
# infect nodes
for n in self.node.keys():
if numpy.random.random() <= self.p_infected:
self.node[n][self.DYNAMICAL_STATE] = self.INFECTED
else:
self.node[n][self.DYNAMICAL_STATE] = self.SUSCEPTIBLE
self.POPULATION = self.calculate_populations()
# extract the initial set of SI edges
for (n, m, data) in self.edges_iter(self.POPULATION[self.INFECTED], data = True):
if self.node[m][self.DYNAMICAL_STATE] == self.SUSCEPTIBLE:
self._si.insert(0, (n, m, data))
# mark all edges as unoccupied
for (n, m, data) in self.edges_iter(data = True):
data[self.OCCUPIED] = False
def after(self):
pass
def at_equilibrium( self):
'''SIR dynamics is at equilibrium if there are no more infected nodes left
in the network, no susceptible nodes adjacent to infected nodes, or if we've
exceeded the default simulation length.
returns: True if the model has stopped'''
if self.CURRENT_TIMESTEP >= self._time_limit:
return True
else:
return ((len(self.POPULATION[self.INFECTED]) == 0))
# or (len(self._si) == 0))
def infect( self ):
'''Infect a node chosen at random from the SI edges.'''
# choose an SI edge
i = numpy.random.randint(len(self._si))
(n, m, data) = self._si[i]
# infect the susceptible end
self.update_node(m,self.SUSCEPTIBLE,self.INFECTED)
# label the edge we traversed as occupied
data[self.OCCUPIED] = True
# remove all edges in the SI list from an infected node to this one
self._si = [ (np, mp, data) for (np, mp, data) in self._si if m != mp ]
# add all the edges incident on this node connected to susceptible nodes
for (_, mp, datap) in self.edges_iter(m, data = True):
if self.node[mp][self.DYNAMICAL_STATE] == self.SUSCEPTIBLE:
self._si.insert(0, (m, mp, datap))
def recover( self ):
'''Cause a node to recover.'''
# choose an infected node at random
i = numpy.random.randint(len(self.POPULATION[self.INFECTED]))
n = self.POPULATION[self.INFECTED][i]
# mark the node as recovered
self.update_node(n,self.INFECTED,self.SUSCEPTIBLE)
# remove all edges in the SI list incident on this node
self._si = [ (np, m, e) for (np, m, e) in self._si if np != n ]
# add all the edges incident on this node connected to infected nodes
for (_, mp, datap) in self.edges_iter(n, data = True):
if self.node[mp][self.DYNAMICAL_STATE] == self.INFECTED:
self._si.insert(0, (mp, n, datap))
def rewire( self ):
'''Cause a node to rewire.'''
# choose an SI edge
i = numpy.random.randint(len(self._si))
# remove the edge from the si list and from the overall graph structure
(n, m, data) = self._si.pop(i)
previous_degree = self.degree(n)
self.remove_edges_from([(n, m)])
self.degree_dict[n] -= 1
# Add a link to a new node
# Clone the list of susceptible and recovered nodes - these will be the candidates for the new link
candidates = list(self.POPULATION[self.SUSCEPTIBLE])
# Remove the current node (don't want self loops)
if m in candidates:
candidates.remove(m)
# Remove neighbours from candidates
neighbours = self.neighbors(m)
candidates = [c for c in candidates if c not in neighbours]
# Create a dict to store how far away each candidate is in terms of degree from the disconnected node
pref_cand = dict()
for c in candidates:
pref_cand[c] = abs(self.degree_dict[c] - previous_degree)
# Reduce the candidates down to only those who are nearest to the disconncected node in terms of degree
min_distance = min(pref_cand.values())
candidates = [c for c in pref_cand if pref_cand[c] == min_distance]
# Pick a candidate
if(len(candidates) >= 1):
new_neighb_index = numpy.random.randint(len(candidates))
self.add_edge(m, candidates[new_neighb_index])
self.degree_dict[candidates[new_neighb_index]] += 1
def transitions( self, t ):
'''Return the transition vector for the dynamics.
t: time (ignored)
returns: the transition vector'''
# transitions are expressed as rates, whereas we're specified
# in terms of probabilities, so we convert the latter to the former.
return [ (len(self._si) * self.p_infect, lambda t: self.infect()),
(len(self.POPULATION[self.INFECTED]) * self.p_recover, lambda t: self.recover()),
(len(self._si) * self.p_rewire, lambda t: self.rewire())]