Skip to content

Commit 26cb14a

Browse files
committed
move to named parameters
1 parent 680ba56 commit 26cb14a

File tree

4 files changed

+60
-31
lines changed

4 files changed

+60
-31
lines changed

src/epidemik/EpiModel.py

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def __init__(self, compartments=None, seed=None, rng=None):
4040
self.population = None
4141
self.orig_comps = None
4242
self.demographics = False
43+
self.params = {}
4344

4445
if seed is None:
4546
seed = int(time.time()) + os.getpid()
@@ -48,11 +49,15 @@ def __init__(self, compartments=None, seed=None, rng=None):
4849
self.rng = np.random.default_rng(seed=seed)
4950
else:
5051
self.rng = rng
51-
52+
5253
if compartments is not None:
5354
self.transitions.add_nodes_from([comp for comp in compartments])
5455

55-
def add_interaction(self, source: str, target: str, agent: str, rate: float) -> None:
56+
def add_interaction(self,
57+
source: str,
58+
target: str,
59+
agent: str,
60+
**rates) -> None:
5661
"""
5762
Add an interaction between two compartments
5863
@@ -63,15 +68,19 @@ def add_interaction(self, source: str, target: str, agent: str, rate: float) ->
6368
Name of the target compartment
6469
- agent: string
6570
Name of the agent
66-
- rate: float
67-
Rate of the interaction
71+
- params: string
72+
Named parameters for the interaction
6873
6974
Returns:
7075
None
71-
"""
72-
self.transitions.add_edge(source, target, agent=agent, rate=rate)
76+
"""
77+
78+
self.params.update(rates)
79+
rates = list(rates.keys())
80+
81+
self.transitions.add_edge(source, target, agent=agent, rate=rates[0])
7382

74-
def add_spontaneous(self, source: str, target: str, rate: float) -> None:
83+
def add_spontaneous(self, source: str, target: str, **rates) -> None:
7584
"""
7685
Add a spontaneous transition between two compartments
7786
@@ -86,7 +95,11 @@ def add_spontaneous(self, source: str, target: str, rate: float) -> None:
8695
Returns:
8796
None
8897
"""
89-
self.transitions.add_edge(source, target, rate=rate)
98+
99+
self.params.update(rates)
100+
rates = list(rates.keys())
101+
102+
self.transitions.add_edge(source, target, rate=rates[0])
90103

91104
def add_birth_rate(self, rate: float, comps: Union[List, None] = None) -> None:
92105
"""
@@ -224,7 +237,8 @@ def _new_cases(self, time: float, population: np.ndarray, pos: Dict) -> np.ndar
224237
target = edge[1]
225238
trans = edge[2]
226239

227-
rate = trans['rate']*population[pos[source]]
240+
rate_val = self.params[trans['rate']]
241+
rate = rate_val*population[pos[source]]
228242

229243
if 'start' in trans and trans['start'] >= time:
230244
continue
@@ -370,7 +384,7 @@ def simulate(self, timesteps: int, t_min: int = 1, seasonality: Union[np.ndarray
370384
source = pos[comp]
371385
target = pos[node_j]
372386

373-
rate = data['rate']
387+
rate = self.params[data['rate']]
374388

375389
if 'start' in data and data['start'] >= t:
376390
continue
@@ -505,21 +519,24 @@ def __repr__(self) -> str:
505519
(self.transitions.number_of_nodes(),
506520
self.transitions.number_of_edges())
507521

522+
text += "Parameters:\n"
523+
for rate, value in self.params.items():
524+
text += "%s= %f\n" % (rate, value)
525+
text += "\n\nTransitions:\n"
526+
508527
for edge in self.transitions.edges(data=True):
509528
source = edge[0]
510529
target = edge[1]
511530
trans = edge[2]
512-
513-
rate = trans['rate']
514-
531+
515532
if 'agent' in trans:
516533
agent = trans['agent']
517-
text += "%s + %s = %s %f\n" % (source, agent, target, rate)
534+
text += "%s + %s = %s %s\n" % (source, agent, target, trans['rate'])
518535
elif 'start' in trans:
519536
start = trans['start']
520-
text+="%s -> %s %f starting at %s days\n" % (source, target, rate, start)
537+
text+="%s -> %s %s starting at %s days\n" % (source, target, rate, start)
521538
else:
522-
text+="%s -> %s %f\n" % (source, target, rate)
539+
text+="%s -> %s %s\n" % (source, target, rate)
523540

524541
R0 = self.R0()
525542

@@ -664,7 +681,7 @@ def R0(self) -> Union[float, None]:
664681

665682
try:
666683
for node_i, node_j, data in self.transitions.edges(data=True):
667-
rate = data['rate']
684+
rate = self.params[data['rate']]
668685

669686
if "agent" in data:
670687
target = pos[node_j]

src/epidemik/MetaEpiModel.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def __repr__(self):
7070
text = "Metapopulation model with %u populations\n\nThe disease is defined by an %s" % (self.travel_graph.shape[0], model_text)
7171
return text
7272

73-
def add_interaction(self, source, target, agent, rate):
73+
def add_interaction(self, source, target, agent, **rates):
7474
"""
7575
Add an interaction between two compartments_
7676
@@ -88,9 +88,9 @@ def add_interaction(self, source, target, agent, rate):
8888
None
8989
"""
9090
for state in self.models:
91-
self.models[state].add_interaction(source, target, agent, rate)
91+
self.models[state].add_interaction(source, target, agent, **rates)
9292

93-
def add_spontaneous(self, source, target, rate):
93+
def add_spontaneous(self, source, target, **rates):
9494
"""
9595
Add a spontaneous transition between two compartments_
9696
@@ -106,9 +106,9 @@ def add_spontaneous(self, source, target, rate):
106106
None
107107
"""
108108
for state in self.models:
109-
self.models[state].add_spontaneous(source, target, rate)
109+
self.models[state].add_spontaneous(source, target, **rates)
110110

111-
def add_vaccination(self, source, target, rate, start):
111+
def add_vaccination(self, source, target, start, **rates):
112112
"""
113113
Add a vaccination transition between two compartments_
114114
@@ -126,7 +126,7 @@ def add_vaccination(self, source, target, rate, start):
126126
None
127127
"""
128128
for state in self.models:
129-
self.models[state].add_vaccination(source, target, rate, start)
129+
self.models[state].add_vaccination(source, target, start, **rates)
130130

131131
def R0(self):
132132
key = list(self.models.keys())[0]

src/epidemik/NetworkEpiModel.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,17 @@ def __init__(self, network, compartments=None):
2020
self.kavg_ = 2*network.number_of_edges()/network.number_of_nodes()
2121
self.spontaneous = {}
2222
self.interactions = {}
23+
self.params = {}
2324

2425
def integrate(self, timesteps, **kwargs):
2526
raise NotImplementedError("Network Models don't support numerical integration")
2627

27-
def add_interaction(self, source, target, agent, rate, rescale=False):
28+
def add_interaction(self, source, target, agent, rescale=False, **rates):
2829
if rescale:
2930
rate /= self.kavg_
3031

32+
self.params.update(rates)
33+
rate = list(rates.keys())[0]
3134
super(NetworkEpiModel, self).add_interaction(source, target, agent=agent, rate=rate)
3235

3336
if source not in self.interactions:
@@ -36,9 +39,13 @@ def add_interaction(self, source, target, agent, rate, rescale=False):
3639
if target not in self.interactions[source]:
3740
self.interactions[source] = {}
3841

42+
3943
self.interactions[source][agent] = {'target': target, 'rate': rate}
4044

41-
def add_spontaneous(self, source, target, rate):
45+
def add_spontaneous(self, source, target, **rates):
46+
self.params.update(rates)
47+
rate = list(rates.keys())[0]
48+
4249
super(NetworkEpiModel, self).add_spontaneous(source, target, rate=rate)
4350
if source not in self.spontaneous:
4451
self.spontaneous[source] = {}
@@ -97,7 +104,8 @@ def simulate(self, timesteps, seeds, **kwargs):
97104
if state_j in infections[state_i]:
98105
prob = self.rng.random()
99106

100-
if prob < infections[state_i][state_j]['rate']:
107+
rate = self.params[infections[state_i][state_j]['rate']]
108+
if prob < rate:
101109
new_state = infections[state_i][state_j]['target']
102110
population[t, node_j] = new_state
103111

@@ -110,7 +118,8 @@ def simulate(self, timesteps, seeds, **kwargs):
110118
prob = np.zeros(len(pos))
111119

112120
for target in self.spontaneous[state_i]:
113-
prob[pos[target]] = self.spontaneous[state_i][target]
121+
rate = self.params[self.spontaneous[state_i]['target']]
122+
prob[pos[target]] = rate
114123

115124
prob[pos[state_i]] = 1-np.sum(prob)
116125

tests/tests_EpiModel.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import unittest
22
from epidemik import EpiModel
3+
import logging
34

45
class EpiModelTestCase(unittest.TestCase):
56
def setUp(self):
67
self.SIR = EpiModel()
78
self.beta = 0.3
89
self.mu = 0.1
9-
self.SIR.add_interaction('S', 'I', 'I', self.beta)
10-
self.SIR.add_spontaneous('I', 'R', self.mu)
10+
self.SIR.add_interaction('S', 'I', 'I', beta=self.beta)
11+
self.SIR.add_spontaneous('I', 'R', mu=self.mu)
1112

1213
def test_R0(self):
1314
self.assertEqual(self.SIR.R0(), 3.0, 'incorrect R0')
@@ -21,10 +22,12 @@ def test_edges(self):
2122

2223
if "agent" in data:
2324
self.assertEqual(data['agent'], 'I')
24-
self.assertEqual(data['rate'], self.beta)
25+
self.assertEqual(data['rate'], 'beta')
26+
self.assertEqual(self.SIR.params['beta'], self.beta)
2527
self.assertEqual(edge[0], 'S')
2628
self.assertEqual(edge[1], 'I')
2729
else:
28-
self.assertEqual(data['rate'], self.mu)
30+
self.assertEqual(data['rate'], 'mu')
31+
self.assertEqual(self.SIR.params['mu'], self.mu)
2932
self.assertEqual(edge[0], 'I')
3033
self.assertEqual(edge[1], 'R')

0 commit comments

Comments
 (0)