pyR0compute 0.1.0__tar.gz

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.
@@ -0,0 +1,2 @@
1
+ .ipynb_checkpoints/
2
+ .DS_Store
@@ -0,0 +1,29 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (unreleased)
4
+
5
+ First packaged release. The code of the original notebook was turned into an installable library.
6
+
7
+ ### Added
8
+ - `R0Model`: models can be entered as text (`dS/dt = ...`), as a dict or as SymPy symbols.
9
+ - Automatic detection of parameters: every symbol that is not a state variable.
10
+ - Equations in any order; infected compartments given by name, symbol or index.
11
+ - Safe parsing of names that are special in SymPy (`I`, `S`, `E`, `N`, `beta`, `gamma`, `lambda`) and `^` for powers.
12
+ - Auxiliary definitions in text models (`N = S + I + R`).
13
+ - Partial user-given disease-free equilibrium (`dfe={"S": "N"}`), list of all non-negative DFEs (`dfe_candidates`).
14
+ - `report_latex()`: step-by-step report in LaTeX (document or Markdown for notebooks).
15
+ - `R0_numeric`, `sensitivity_indices`, `report`, `latex`, `eigenvalues`, `next_generation_matrix_small`.
16
+ - Informative exceptions (`ModelSpecificationError`, `DiseaseFreeEquilibriumError`, `NextGenerationError`).
17
+ - Test suite (SIR, SEIR, vector-borne, literature models) and continuous integration.
18
+
19
+ ### Fixed
20
+ - Terms with a division (e.g. `beta*S*I/N`) were never recognised as new infections, so R0 was silently returned as 0. The sign test `term.has(-1)` matched the exponent of `1/N`.
21
+ - Coefficients such as `(1 - p)*beta*S*I` were split into a new-infection part and a spurious transition part, giving a wrong R0.
22
+ - For vector-borne models the negative eigenvalue `-sqrt(...)` could be returned as R0; the Perron root is now used.
23
+ - When the dominant eigenvalue depends on the parameters (competing strains), R0 is returned as `Max(...)` instead of an arbitrary branch.
24
+ - Undetermined DFE values were replaced by unexplained symbols such as `S_eq`; an error now says which value must be given.
25
+ - Several DFEs: the first solution returned by SymPy was taken silently.
26
+
27
+ ### Changed
28
+ - `GeneralEpidemiologicalModel` keeps the original interface; `parameters` is now optional and the infected equations no longer need to come first.
29
+ - Output is silent by default (`verbose=True` or `report()` for the step-by-step description).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yvan Baldera-Moreno
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,146 @@
1
+ Metadata-Version: 2.5
2
+ Name: pyR0compute
3
+ Version: 0.1.0
4
+ Summary: Symbolic computation of the basic reproduction number R0 via the next-generation matrix method
5
+ Project-URL: Homepage, https://github.com/Yvan-BM-Git/pyR0compute
6
+ Project-URL: Issues, https://github.com/Yvan-BM-Git/pyR0compute/issues
7
+ Author: Yvan Baldera-Moreno
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: R0,basic reproduction number,compartmental models,epidemiology,mathematical immunology,next-generation matrix,symbolic computation,sympy
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Education
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
18
+ Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
19
+ Requires-Python: >=3.9
20
+ Requires-Dist: numpy>=1.22
21
+ Requires-Dist: sympy>=1.12
22
+ Provides-Extra: test
23
+ Requires-Dist: pytest>=7; extra == 'test'
24
+ Description-Content-Type: text/markdown
25
+
26
+ # pyR0compute
27
+
28
+ Symbolic computation of the basic reproduction number $R_0$ of compartmental ODE models by the next-generation matrix method (van den Driessche & Watmough, 2002).
29
+
30
+ Write the model, say which compartments are infected, and pyR0compute does the rest: every symbol that is not a state variable is treated as a parameter, the new-infection terms $\mathcal{F}$ and transitions $\mathcal{V}$ are identified, the disease-free equilibrium (DFE) is solved, and $R_0 = \rho(FV^{-1})$ is returned as a SymPy expression.
31
+
32
+ [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Yvan-BM-Git/pyR0compute/blob/main/examples/pyR0compute_examples.ipynb)
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pip install pyR0compute
38
+ ```
39
+
40
+ Until the first release is on PyPI, install it from GitHub:
41
+
42
+ ```bash
43
+ pip install git+https://github.com/Yvan-BM-Git/pyR0compute
44
+ ```
45
+
46
+ Requires Python ≥ 3.9, SymPy and NumPy.
47
+
48
+ ## Quick start
49
+
50
+ ```python
51
+ from pyr0compute import R0Model
52
+
53
+ model = R0Model("""
54
+ dS/dt = Lambda - beta*S*I - mu*S
55
+ dE/dt = beta*S*I - (sigma + mu)*E
56
+ dI/dt = sigma*E - (gamma + mu)*I
57
+ dR/dt = gamma*I - mu*R
58
+ """, infected=["E", "I"])
59
+
60
+ model.parameters # (Lambda, beta, gamma, mu, sigma) <- detected automatically
61
+ model.dfe # {S: Lambda/mu, E: 0, I: 0, R: 0}
62
+ model.R0 # Lambda*beta*sigma/(mu*(gamma + mu)*(mu + sigma))
63
+ ```
64
+
65
+ The equations can be written in any order; there is no need to list the infected compartments first.
66
+
67
+ ### Three ways to enter a model
68
+
69
+ ```python
70
+ # 1. Text: one "dX/dt = ..." (or "X' = ...") line per variable.
71
+ # Auxiliary lines such as "N = S + I + R" are substituted.
72
+ R0Model("""
73
+ N = S + I + R
74
+ dS/dt = Lambda - beta*S*I/N - mu*S
75
+ dI/dt = beta*S*I/N - (gamma + mu)*I
76
+ dR/dt = gamma*I - mu*R
77
+ """, infected=["I"])
78
+
79
+ # 2. A dict {variable: right-hand side} (strings or SymPy expressions)
80
+ R0Model({"S": "Lambda - beta*S*I - mu*S",
81
+ "I": "beta*S*I - (gamma + mu)*I"}, infected=["I"])
82
+
83
+ # 3. SymPy symbols
84
+ import sympy as sp
85
+ S, I = sp.symbols("S I")
86
+ beta, gamma, mu, Lam = sp.symbols("beta gamma mu Lambda")
87
+ R0Model({S: Lam - beta*S*I - mu*S, I: beta*S*I - (gamma + mu)*I}, infected=[I])
88
+ ```
89
+
90
+ Use subscripts in names for clean LaTeX: `mu_h`, `alpha_hv`, `Delta_h` are written $\mu_h$, $\alpha_{hv}$, $\Delta_h$. Names that are special in SymPy (`I`, `S`, `E`, `N`, `beta`, `gamma`, `Lambda`, even `lambda`) are plain symbols inside text models, and `^` means a power.
91
+
92
+ ### What you get
93
+
94
+ | Attribute / method | Content |
95
+ |---|---|
96
+ | `R0` | Basic reproduction number (spectral radius of $FV^{-1}$) |
97
+ | `parameters` | Parameters detected automatically |
98
+ | `new_infections`, `transitions` | Terms $\mathcal{F}_i$ and $\mathcal{V}_i$ of each infected compartment |
99
+ | `dfe`, `dfe_candidates` | Disease-free equilibrium used, and all non-negative ones found |
100
+ | `F`, `V`, `K` | Jacobians at the DFE and next-generation matrix $K = FV^{-1}$ |
101
+ | `next_generation_matrix_small` | $K$ restricted to compartments receiving new infections |
102
+ | `eigenvalues` | Eigenvalues of $K$ |
103
+ | `R0_numeric(values)` | Numerical spectral radius (for large models without a closed form) |
104
+ | `sensitivity_indices(values=None)` | Normalized forward sensitivity indices $\Upsilon_p = \frac{\partial R_0}{\partial p}\frac{p}{R_0}$ |
105
+ | `report()` | Step-by-step description of the whole computation |
106
+ | `report_latex(style, standalone, mat_str)` | The same report in LaTeX (`"document"`, compilable with `standalone=True`) or Markdown for notebooks (`"markdown"`) |
107
+ | `latex()` | LaTeX code of $R_0$ |
108
+
109
+ ### When the automatic choices need help
110
+
111
+ * **Closed populations** (no births), e.g. the classic SIR: the DFE is not unique, so give it, `dfe={"S": "N"}`. The error message says which value is missing.
112
+ * **Several DFEs** (e.g. logistic vector populations): the one with the most non-zero compartments is used and a warning is shown. Choose another with `dfe={...}`; all are in `model.dfe_candidates`.
113
+ * **Custom decompositions**: the split $\mathcal{F}$/$\mathcal{V}$ is not unique. Override the automatic one with `new_infections={"E": "beta*S*I/N"}`.
114
+
115
+ ### How new infections are detected
116
+
117
+ In the equation of an infected compartment, a positive term is a new infection when it involves an infected compartment and either (i) it is lost from an uninfected compartment (a transfer such as $S \to E$), or (ii) it involves an uninfected compartment and is not a transfer between infected compartments. Progression ($E \to I$), treatment failure, superinfection between strains, deaths and recoveries go to $\mathcal{V}$. `model.report()` shows the classification of every term and the reason.
118
+
119
+ ## Validation
120
+
121
+ The test suite (`pytest`) reproduces known results: SIR (with and without vital dynamics, mass-action and frequency-dependent), SEIR, a vaccination model, Ross-Macdonald, a host-vector SEIR/SEI model with human-to-human transmission and logistic vectors, the treatment model of van den Driessche & Watmough (2002, §4.1), within-host target cell-infected cell-virus models and a two-strain model with superinfection. Symbolic results are also checked against the numerical spectral radius.
122
+
123
+ ## Previous interface
124
+
125
+ Code written for the original notebook keeps working:
126
+
127
+ ```python
128
+ from pyr0compute import GeneralEpidemiologicalModel
129
+ model = GeneralEpidemiologicalModel(variables, parameters, equations, infected_indices)
130
+ model.calculate_R0()
131
+ ```
132
+
133
+ ## Citation
134
+
135
+ If you use pyR0compute in your research, please cite it (see `CITATION.cff`) together with:
136
+
137
+ * van den Driessche, P., & Watmough, J. (2002). Reproduction numbers and sub-threshold endemic equilibria for compartmental models of disease transmission. *Mathematical Biosciences*, 180(1-2), 29-48.
138
+ * Diekmann, O., Heesterbeek, J. A. P., & Roberts, M. G. (2010). The construction of next-generation matrices for compartmental epidemic models. *Journal of the Royal Society Interface*, 7(47), 873-885.
139
+
140
+ ## Resumen en español
141
+
142
+ pyR0compute calcula simbólicamente el número básico de reproducción $R_0$ mediante el método de la matriz de próxima generación. Basta escribir el sistema de EDO e indicar los compartimentos infectados: todos los demás símbolos se reconocen automáticamente como parámetros, se identifican los términos de nuevas infecciones, se calcula el equilibrio libre de enfermedad y se obtiene $R_0$. El notebook `examples/pyR0compute_examples.ipynb` contiene ejemplos listos para Google Colab.
143
+
144
+ ## License
145
+
146
+ MIT, see `LICENSE`.
@@ -0,0 +1,121 @@
1
+ # pyR0compute
2
+
3
+ Symbolic computation of the basic reproduction number $R_0$ of compartmental ODE models by the next-generation matrix method (van den Driessche & Watmough, 2002).
4
+
5
+ Write the model, say which compartments are infected, and pyR0compute does the rest: every symbol that is not a state variable is treated as a parameter, the new-infection terms $\mathcal{F}$ and transitions $\mathcal{V}$ are identified, the disease-free equilibrium (DFE) is solved, and $R_0 = \rho(FV^{-1})$ is returned as a SymPy expression.
6
+
7
+ [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Yvan-BM-Git/pyR0compute/blob/main/examples/pyR0compute_examples.ipynb)
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ pip install pyR0compute
13
+ ```
14
+
15
+ Until the first release is on PyPI, install it from GitHub:
16
+
17
+ ```bash
18
+ pip install git+https://github.com/Yvan-BM-Git/pyR0compute
19
+ ```
20
+
21
+ Requires Python ≥ 3.9, SymPy and NumPy.
22
+
23
+ ## Quick start
24
+
25
+ ```python
26
+ from pyr0compute import R0Model
27
+
28
+ model = R0Model("""
29
+ dS/dt = Lambda - beta*S*I - mu*S
30
+ dE/dt = beta*S*I - (sigma + mu)*E
31
+ dI/dt = sigma*E - (gamma + mu)*I
32
+ dR/dt = gamma*I - mu*R
33
+ """, infected=["E", "I"])
34
+
35
+ model.parameters # (Lambda, beta, gamma, mu, sigma) <- detected automatically
36
+ model.dfe # {S: Lambda/mu, E: 0, I: 0, R: 0}
37
+ model.R0 # Lambda*beta*sigma/(mu*(gamma + mu)*(mu + sigma))
38
+ ```
39
+
40
+ The equations can be written in any order; there is no need to list the infected compartments first.
41
+
42
+ ### Three ways to enter a model
43
+
44
+ ```python
45
+ # 1. Text: one "dX/dt = ..." (or "X' = ...") line per variable.
46
+ # Auxiliary lines such as "N = S + I + R" are substituted.
47
+ R0Model("""
48
+ N = S + I + R
49
+ dS/dt = Lambda - beta*S*I/N - mu*S
50
+ dI/dt = beta*S*I/N - (gamma + mu)*I
51
+ dR/dt = gamma*I - mu*R
52
+ """, infected=["I"])
53
+
54
+ # 2. A dict {variable: right-hand side} (strings or SymPy expressions)
55
+ R0Model({"S": "Lambda - beta*S*I - mu*S",
56
+ "I": "beta*S*I - (gamma + mu)*I"}, infected=["I"])
57
+
58
+ # 3. SymPy symbols
59
+ import sympy as sp
60
+ S, I = sp.symbols("S I")
61
+ beta, gamma, mu, Lam = sp.symbols("beta gamma mu Lambda")
62
+ R0Model({S: Lam - beta*S*I - mu*S, I: beta*S*I - (gamma + mu)*I}, infected=[I])
63
+ ```
64
+
65
+ Use subscripts in names for clean LaTeX: `mu_h`, `alpha_hv`, `Delta_h` are written $\mu_h$, $\alpha_{hv}$, $\Delta_h$. Names that are special in SymPy (`I`, `S`, `E`, `N`, `beta`, `gamma`, `Lambda`, even `lambda`) are plain symbols inside text models, and `^` means a power.
66
+
67
+ ### What you get
68
+
69
+ | Attribute / method | Content |
70
+ |---|---|
71
+ | `R0` | Basic reproduction number (spectral radius of $FV^{-1}$) |
72
+ | `parameters` | Parameters detected automatically |
73
+ | `new_infections`, `transitions` | Terms $\mathcal{F}_i$ and $\mathcal{V}_i$ of each infected compartment |
74
+ | `dfe`, `dfe_candidates` | Disease-free equilibrium used, and all non-negative ones found |
75
+ | `F`, `V`, `K` | Jacobians at the DFE and next-generation matrix $K = FV^{-1}$ |
76
+ | `next_generation_matrix_small` | $K$ restricted to compartments receiving new infections |
77
+ | `eigenvalues` | Eigenvalues of $K$ |
78
+ | `R0_numeric(values)` | Numerical spectral radius (for large models without a closed form) |
79
+ | `sensitivity_indices(values=None)` | Normalized forward sensitivity indices $\Upsilon_p = \frac{\partial R_0}{\partial p}\frac{p}{R_0}$ |
80
+ | `report()` | Step-by-step description of the whole computation |
81
+ | `report_latex(style, standalone, mat_str)` | The same report in LaTeX (`"document"`, compilable with `standalone=True`) or Markdown for notebooks (`"markdown"`) |
82
+ | `latex()` | LaTeX code of $R_0$ |
83
+
84
+ ### When the automatic choices need help
85
+
86
+ * **Closed populations** (no births), e.g. the classic SIR: the DFE is not unique, so give it, `dfe={"S": "N"}`. The error message says which value is missing.
87
+ * **Several DFEs** (e.g. logistic vector populations): the one with the most non-zero compartments is used and a warning is shown. Choose another with `dfe={...}`; all are in `model.dfe_candidates`.
88
+ * **Custom decompositions**: the split $\mathcal{F}$/$\mathcal{V}$ is not unique. Override the automatic one with `new_infections={"E": "beta*S*I/N"}`.
89
+
90
+ ### How new infections are detected
91
+
92
+ In the equation of an infected compartment, a positive term is a new infection when it involves an infected compartment and either (i) it is lost from an uninfected compartment (a transfer such as $S \to E$), or (ii) it involves an uninfected compartment and is not a transfer between infected compartments. Progression ($E \to I$), treatment failure, superinfection between strains, deaths and recoveries go to $\mathcal{V}$. `model.report()` shows the classification of every term and the reason.
93
+
94
+ ## Validation
95
+
96
+ The test suite (`pytest`) reproduces known results: SIR (with and without vital dynamics, mass-action and frequency-dependent), SEIR, a vaccination model, Ross-Macdonald, a host-vector SEIR/SEI model with human-to-human transmission and logistic vectors, the treatment model of van den Driessche & Watmough (2002, §4.1), within-host target cell-infected cell-virus models and a two-strain model with superinfection. Symbolic results are also checked against the numerical spectral radius.
97
+
98
+ ## Previous interface
99
+
100
+ Code written for the original notebook keeps working:
101
+
102
+ ```python
103
+ from pyr0compute import GeneralEpidemiologicalModel
104
+ model = GeneralEpidemiologicalModel(variables, parameters, equations, infected_indices)
105
+ model.calculate_R0()
106
+ ```
107
+
108
+ ## Citation
109
+
110
+ If you use pyR0compute in your research, please cite it (see `CITATION.cff`) together with:
111
+
112
+ * van den Driessche, P., & Watmough, J. (2002). Reproduction numbers and sub-threshold endemic equilibria for compartmental models of disease transmission. *Mathematical Biosciences*, 180(1-2), 29-48.
113
+ * Diekmann, O., Heesterbeek, J. A. P., & Roberts, M. G. (2010). The construction of next-generation matrices for compartmental epidemic models. *Journal of the Royal Society Interface*, 7(47), 873-885.
114
+
115
+ ## Resumen en español
116
+
117
+ pyR0compute calcula simbólicamente el número básico de reproducción $R_0$ mediante el método de la matriz de próxima generación. Basta escribir el sistema de EDO e indicar los compartimentos infectados: todos los demás símbolos se reconocen automáticamente como parámetros, se identifican los términos de nuevas infecciones, se calcula el equilibrio libre de enfermedad y se obtiene $R_0$. El notebook `examples/pyR0compute_examples.ipynb` contiene ejemplos listos para Google Colab.
118
+
119
+ ## License
120
+
121
+ MIT, see `LICENSE`.
@@ -0,0 +1,48 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.21"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "pyR0compute"
7
+ dynamic = ["version"]
8
+ description = "Symbolic computation of the basic reproduction number R0 via the next-generation matrix method"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ authors = [{ name = "Yvan Baldera-Moreno" }]
13
+ requires-python = ">=3.9"
14
+ dependencies = ["sympy>=1.12", "numpy>=1.22"]
15
+ keywords = [
16
+ "basic reproduction number", "R0", "next-generation matrix", "epidemiology",
17
+ "mathematical immunology", "compartmental models", "symbolic computation", "sympy",
18
+ ]
19
+ classifiers = [
20
+ "Development Status :: 4 - Beta",
21
+ "Intended Audience :: Science/Research",
22
+ "Intended Audience :: Education",
23
+ "Operating System :: OS Independent",
24
+ "Programming Language :: Python :: 3",
25
+ "Programming Language :: Python :: 3 :: Only",
26
+ "Topic :: Scientific/Engineering :: Mathematics",
27
+ "Topic :: Scientific/Engineering :: Medical Science Apps.",
28
+ ]
29
+
30
+ [project.optional-dependencies]
31
+ test = ["pytest>=7"]
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/Yvan-BM-Git/pyR0compute"
35
+ Issues = "https://github.com/Yvan-BM-Git/pyR0compute/issues"
36
+
37
+ [tool.hatch.version]
38
+ path = "src/pyr0compute/__init__.py"
39
+
40
+ [tool.hatch.build.targets.wheel]
41
+ packages = ["src/pyr0compute"]
42
+
43
+ [tool.hatch.build.targets.sdist]
44
+ include = ["src/pyr0compute", "tests", "README.md", "LICENSE", "CHANGELOG.md"]
45
+
46
+ [tool.pytest.ini_options]
47
+ testpaths = ["tests"]
48
+ addopts = "-ra"
@@ -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
+ ]
@@ -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."""