pyR0compute 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyr0compute/__init__.py +39 -0
- pyr0compute/compat.py +79 -0
- pyr0compute/exceptions.py +17 -0
- pyr0compute/model.py +867 -0
- pyr0compute/parsing.py +202 -0
- pyr0compute-0.1.0.dist-info/METADATA +146 -0
- pyr0compute-0.1.0.dist-info/RECORD +9 -0
- pyr0compute-0.1.0.dist-info/WHEEL +4 -0
- pyr0compute-0.1.0.dist-info/licenses/LICENSE +21 -0
pyr0compute/__init__.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""pyR0compute: symbolic computation of the basic reproduction number R0.
|
|
2
|
+
|
|
3
|
+
Implements the next-generation matrix method of van den Driessche & Watmough
|
|
4
|
+
(2002) for compartmental ODE models. Write the model, say which compartments
|
|
5
|
+
are infected, and every other symbol is treated as a parameter::
|
|
6
|
+
|
|
7
|
+
from pyr0compute import R0Model
|
|
8
|
+
|
|
9
|
+
model = R0Model('''
|
|
10
|
+
dS/dt = Lambda - beta*S*I - mu*S
|
|
11
|
+
dI/dt = beta*S*I - (gamma + mu)*I
|
|
12
|
+
dR/dt = gamma*I - mu*R
|
|
13
|
+
''', infected=["I"])
|
|
14
|
+
|
|
15
|
+
model.R0 # Lambda*beta/(mu*(gamma + mu))
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from .compat import GeneralEpidemiologicalModel
|
|
19
|
+
from .exceptions import (
|
|
20
|
+
DiseaseFreeEquilibriumError,
|
|
21
|
+
ModelSpecificationError,
|
|
22
|
+
NextGenerationError,
|
|
23
|
+
R0ComputeError,
|
|
24
|
+
)
|
|
25
|
+
from .model import R0Model
|
|
26
|
+
from .parsing import parse_expression
|
|
27
|
+
|
|
28
|
+
__version__ = "0.1.0"
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"R0Model",
|
|
32
|
+
"GeneralEpidemiologicalModel",
|
|
33
|
+
"parse_expression",
|
|
34
|
+
"R0ComputeError",
|
|
35
|
+
"ModelSpecificationError",
|
|
36
|
+
"DiseaseFreeEquilibriumError",
|
|
37
|
+
"NextGenerationError",
|
|
38
|
+
"__version__",
|
|
39
|
+
]
|
pyr0compute/compat.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Backward-compatible interface of the original notebook."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Optional, Sequence
|
|
6
|
+
|
|
7
|
+
import sympy as sp
|
|
8
|
+
|
|
9
|
+
from .model import R0Model
|
|
10
|
+
|
|
11
|
+
__all__ = ["GeneralEpidemiologicalModel"]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GeneralEpidemiologicalModel(R0Model):
|
|
15
|
+
"""Interface of the original ``pyR0compute_examples.ipynb`` notebook.
|
|
16
|
+
|
|
17
|
+
Existing code keeps working, with two differences: the equations no longer
|
|
18
|
+
need to list the infected compartments first, and ``parameters`` is
|
|
19
|
+
optional (it is detected automatically). New code should use
|
|
20
|
+
:class:`R0Model`.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
variables, parameters, equations, infected_indices
|
|
25
|
+
As in the original notebook; ``infected_indices`` are positions in
|
|
26
|
+
``variables``.
|
|
27
|
+
new_infection_terms
|
|
28
|
+
Optional list aligned with ``variables`` (zeros for uninfected).
|
|
29
|
+
equilibrium_point
|
|
30
|
+
Optional disease-free equilibrium aligned with ``variables``.
|
|
31
|
+
total_population
|
|
32
|
+
Accepted for compatibility; not needed.
|
|
33
|
+
verbose
|
|
34
|
+
Print the step-by-step report when :meth:`calculate_R0` is called
|
|
35
|
+
(default ``True``, like the original).
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
variables: Sequence[sp.Symbol],
|
|
41
|
+
parameters: Optional[Sequence[sp.Symbol]],
|
|
42
|
+
equations: Sequence[sp.Expr],
|
|
43
|
+
infected_indices: Sequence[int],
|
|
44
|
+
new_infection_terms: Optional[Sequence[sp.Expr]] = None,
|
|
45
|
+
equilibrium_point: Optional[Sequence[sp.Expr]] = None,
|
|
46
|
+
total_population=None,
|
|
47
|
+
verbose: bool = True,
|
|
48
|
+
**kwargs,
|
|
49
|
+
) -> None:
|
|
50
|
+
dfe = None
|
|
51
|
+
if equilibrium_point is not None:
|
|
52
|
+
dfe = dict(zip(variables, list(equilibrium_point)))
|
|
53
|
+
self._legacy_verbose = verbose
|
|
54
|
+
super().__init__(
|
|
55
|
+
equations=list(equations),
|
|
56
|
+
infected=list(infected_indices),
|
|
57
|
+
variables=list(variables),
|
|
58
|
+
parameters=list(parameters) if parameters else None,
|
|
59
|
+
new_infections=list(new_infection_terms) if new_infection_terms is not None else None,
|
|
60
|
+
dfe=dfe,
|
|
61
|
+
**kwargs,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def identify_F_V_terms(self):
|
|
65
|
+
"""Return the vectors of new-infection and transition terms."""
|
|
66
|
+
F = sp.Matrix([self.new_infections[v] for v in self.infected])
|
|
67
|
+
V = sp.Matrix([self.transitions[v] for v in self.infected])
|
|
68
|
+
return F, V
|
|
69
|
+
|
|
70
|
+
def calculate_next_generation_matrices(self):
|
|
71
|
+
"""Return ``(F, V)`` evaluated at the disease-free equilibrium."""
|
|
72
|
+
return self.F, self.V
|
|
73
|
+
|
|
74
|
+
def calculate_R0(self):
|
|
75
|
+
"""Return R0 (printing the report if ``verbose``)."""
|
|
76
|
+
R0 = self.R0
|
|
77
|
+
if self._legacy_verbose:
|
|
78
|
+
print(self.report())
|
|
79
|
+
return R0
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Exceptions raised by pyR0compute."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class R0ComputeError(ValueError):
|
|
5
|
+
"""Base class for all pyR0compute errors."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ModelSpecificationError(R0ComputeError):
|
|
9
|
+
"""The model (equations, variables, infected compartments...) is ill-defined."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DiseaseFreeEquilibriumError(R0ComputeError):
|
|
13
|
+
"""The disease-free equilibrium could not be determined unambiguously."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class NextGenerationError(R0ComputeError):
|
|
17
|
+
"""The next-generation matrix or its spectral radius could not be computed."""
|