phylogenie 1.0.4__tar.gz → 1.0.6__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.
Files changed (70) hide show
  1. {phylogenie-1.0.4 → phylogenie-1.0.6}/PKG-INFO +2 -2
  2. {phylogenie-1.0.4 → phylogenie-1.0.6}/README.md +1 -1
  3. phylogenie-1.0.6/phylogenie/backend/__pycache__/__init__.cpython-310.pyc +0 -0
  4. phylogenie-1.0.6/phylogenie/backend/__pycache__/treesimulator.cpython-310.pyc +0 -0
  5. phylogenie-1.0.6/phylogenie/backend/remaster/__pycache__/__init__.cpython-310.pyc +0 -0
  6. phylogenie-1.0.6/phylogenie/backend/remaster/__pycache__/generate.cpython-310.pyc +0 -0
  7. phylogenie-1.0.6/phylogenie/backend/remaster/__pycache__/reactions.cpython-310.pyc +0 -0
  8. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/backend/treesimulator.py +27 -4
  9. phylogenie-1.0.6/phylogenie/core/__pycache__/__init__.cpython-310.pyc +0 -0
  10. phylogenie-1.0.6/phylogenie/core/__pycache__/configs.cpython-310.pyc +0 -0
  11. phylogenie-1.0.6/phylogenie/core/__pycache__/dataset.cpython-310.pyc +0 -0
  12. phylogenie-1.0.6/phylogenie/core/__pycache__/distributions.cpython-310.pyc +0 -0
  13. phylogenie-1.0.6/phylogenie/core/__pycache__/factories.cpython-310.pyc +0 -0
  14. phylogenie-1.0.6/phylogenie/core/__pycache__/typeguards.cpython-310.pyc +0 -0
  15. phylogenie-1.0.6/phylogenie/core/__pycache__/typings.cpython-310.pyc +0 -0
  16. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/core/configs.py +1 -4
  17. phylogenie-1.0.6/phylogenie/core/context/__pycache__/__init__.cpython-310.pyc +0 -0
  18. phylogenie-1.0.6/phylogenie/core/context/__pycache__/configs.cpython-310.pyc +0 -0
  19. phylogenie-1.0.6/phylogenie/core/context/__pycache__/distributions.cpython-310.pyc +0 -0
  20. phylogenie-1.0.6/phylogenie/core/context/__pycache__/factories.cpython-310.pyc +0 -0
  21. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/core/context/configs.py +4 -14
  22. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/core/context/distributions.py +3 -3
  23. phylogenie-1.0.6/phylogenie/core/context/factories.py +54 -0
  24. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/core/factories.py +22 -64
  25. phylogenie-1.0.6/phylogenie/core/msas/__pycache__/__init__.cpython-310.pyc +0 -0
  26. phylogenie-1.0.6/phylogenie/core/msas/__pycache__/alisim.cpython-310.pyc +0 -0
  27. phylogenie-1.0.6/phylogenie/core/msas/__pycache__/base.cpython-310.pyc +0 -0
  28. phylogenie-1.0.6/phylogenie/core/msas/base.py +34 -0
  29. phylogenie-1.0.6/phylogenie/core/trees/__pycache__/__init__.cpython-310.pyc +0 -0
  30. phylogenie-1.0.6/phylogenie/core/trees/__pycache__/base.cpython-310.pyc +0 -0
  31. phylogenie-1.0.6/phylogenie/core/trees/__pycache__/treesimulator.cpython-310.pyc +0 -0
  32. phylogenie-1.0.6/phylogenie/core/trees/remaster/__pycache__/__init__.cpython-310.pyc +0 -0
  33. phylogenie-1.0.6/phylogenie/core/trees/remaster/__pycache__/configs.cpython-310.pyc +0 -0
  34. phylogenie-1.0.6/phylogenie/core/trees/remaster/__pycache__/factories.cpython-310.pyc +0 -0
  35. phylogenie-1.0.6/phylogenie/core/trees/remaster/__pycache__/generator.cpython-310.pyc +0 -0
  36. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/core/trees/remaster/generator.py +0 -3
  37. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/core/trees/treesimulator.py +51 -17
  38. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/core/typeguards.py +12 -7
  39. phylogenie-1.0.6/phylogenie/skyline/__pycache__/__init__.cpython-310.pyc +0 -0
  40. phylogenie-1.0.6/phylogenie/skyline/__pycache__/matrix.cpython-310.pyc +0 -0
  41. phylogenie-1.0.6/phylogenie/skyline/__pycache__/parameter.cpython-310.pyc +0 -0
  42. phylogenie-1.0.6/phylogenie/skyline/__pycache__/vector.cpython-310.pyc +0 -0
  43. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/skyline/matrix.py +43 -21
  44. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/skyline/parameter.py +4 -1
  45. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/skyline/vector.py +25 -3
  46. {phylogenie-1.0.4 → phylogenie-1.0.6}/pyproject.toml +1 -1
  47. phylogenie-1.0.4/phylogenie/core/context/factories.py +0 -41
  48. phylogenie-1.0.4/phylogenie/core/msas/base.py +0 -51
  49. {phylogenie-1.0.4 → phylogenie-1.0.6}/LICENSE.txt +0 -0
  50. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/__init__.py +0 -0
  51. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/backend/__init__.py +0 -0
  52. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/backend/remaster/__init__.py +0 -0
  53. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/backend/remaster/generate.py +0 -0
  54. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/backend/remaster/reactions.py +0 -0
  55. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/configs.py +0 -0
  56. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/core/__init__.py +0 -0
  57. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/core/context/__init__.py +0 -0
  58. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/core/dataset.py +0 -0
  59. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/core/msas/__init__.py +0 -0
  60. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/core/msas/alisim.py +0 -0
  61. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/core/trees/__init__.py +0 -0
  62. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/core/trees/base.py +0 -0
  63. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/core/trees/remaster/__init__.py +0 -0
  64. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/core/trees/remaster/configs.py +0 -0
  65. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/core/trees/remaster/factories.py +0 -0
  66. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/main.py +0 -0
  67. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/py.typed +0 -0
  68. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/skyline/__init__.py +0 -0
  69. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/typeguards.py +0 -0
  70. {phylogenie-1.0.4 → phylogenie-1.0.6}/phylogenie/typings.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: phylogenie
3
- Version: 1.0.4
3
+ Version: 1.0.6
4
4
  Summary: Generate phylogenetic datasets with minimal setup effort
5
5
  Author: Gabriele Marino
6
6
  Author-email: gabmarino.8601@gmail.com
@@ -46,7 +46,7 @@ Phylogenie comes packed with useful features, including:
46
46
  Simply specify the number of cores to use, and Phylogenie handles multiprocessing automatically.
47
47
 
48
48
  - **Pre-implemented parameterizations** 🎯
49
- Include canonical, fossilized birth-death, epidemiological, birth-death with exposed-infectious (BDEI), contact-tracing (CT), and more.
49
+ Include canonical, fossilized birth-death, epidemiological, birth-death with exposed-infectious (BDEI), birth-death with superspreading (BDSS), contact-tracing (CT), and more.
50
50
 
51
51
  - **Skyline parameter support** 🪜
52
52
  Support for piece-wise constant parameters.
@@ -27,7 +27,7 @@ Phylogenie comes packed with useful features, including:
27
27
  Simply specify the number of cores to use, and Phylogenie handles multiprocessing automatically.
28
28
 
29
29
  - **Pre-implemented parameterizations** 🎯
30
- Include canonical, fossilized birth-death, epidemiological, birth-death with exposed-infectious (BDEI), contact-tracing (CT), and more.
30
+ Include canonical, fossilized birth-death, epidemiological, birth-death with exposed-infectious (BDEI), birth-death with superspreading (BDSS), contact-tracing (CT), and more.
31
31
 
32
32
  - **Skyline parameter support** 🪜
33
33
  Support for piece-wise constant parameters.
@@ -17,15 +17,16 @@ from phylogenie.skyline import (
17
17
  DEFAULT_POPULATION = "X"
18
18
  INFECTIOUS_POPULATION = "I"
19
19
  EXPOSED_POPULATION = "E"
20
+ SUPERSPREADER_POPULATION = "S"
20
21
 
21
22
 
22
23
  @dataclass
23
24
  class TreeParams:
24
- populations: str | list[str] = DEFAULT_POPULATION
25
+ populations: str | list[str]
26
+ transmission_rates: SkylineMatrixCoercible
27
+ removal_rates: SkylineVectorCoercible
28
+ sampling_proportions: SkylineVectorCoercible
25
29
  transition_rates: SkylineMatrixCoercible = 0
26
- transmission_rates: SkylineMatrixCoercible = 0
27
- removal_rates: SkylineVectorCoercible = 0
28
- sampling_proportions: SkylineVectorCoercible = 0
29
30
 
30
31
 
31
32
  def generate_tree(
@@ -137,3 +138,25 @@ def get_BDEI_params(
137
138
  removal_rates=removal_rates,
138
139
  sampling_proportions=sampling_proportions,
139
140
  )
141
+
142
+
143
+ def get_BDSS_params(
144
+ reproduction_number: SkylineParameterLike,
145
+ infectious_period: SkylineParameterLike,
146
+ superspreading_ratio: SkylineParameterLike,
147
+ superspreaders_proportion: SkylineParameterLike,
148
+ sampling_proportion: SkylineParameterLike,
149
+ ) -> TreeParams:
150
+ gamma = 1 / infectious_period
151
+ f_SS = superspreaders_proportion
152
+ r_SS = superspreading_ratio
153
+ lambda_IS = reproduction_number * gamma * f_SS / (1 + r_SS * f_SS - f_SS)
154
+ lambda_SI = (reproduction_number * gamma - r_SS * lambda_IS) * r_SS
155
+ lambda_SS = r_SS * lambda_IS
156
+ lambda_II = lambda_SI / r_SS
157
+ return TreeParams(
158
+ populations=[INFECTIOUS_POPULATION, SUPERSPREADER_POPULATION],
159
+ transmission_rates=[[lambda_II, lambda_IS], [lambda_SI, lambda_SS]],
160
+ removal_rates=gamma,
161
+ sampling_proportions=sampling_proportion,
162
+ )
@@ -25,9 +25,6 @@ class SkylineVectorValueModel(StrictBaseModel):
25
25
  SkylineVectorCoercibleConfig = (
26
26
  str | pgt.Scalar | list[SkylineParameterLikeConfig] | SkylineVectorValueModel
27
27
  )
28
- SkylineVectorLikeConfig = (
29
- str | list[SkylineParameterLikeConfig] | SkylineVectorValueModel
30
- )
31
28
 
32
29
 
33
30
  class SkylineMatrixValueModel(StrictBaseModel):
@@ -36,5 +33,5 @@ class SkylineMatrixValueModel(StrictBaseModel):
36
33
 
37
34
 
38
35
  SkylineMatrixCoercibleConfig = (
39
- str | pgt.Scalar | list[SkylineVectorLikeConfig] | SkylineMatrixValueModel
36
+ str | pgt.Scalar | list[SkylineVectorCoercibleConfig] | SkylineMatrixValueModel
40
37
  )
@@ -1,7 +1,3 @@
1
- from typing import Annotated, Literal
2
-
3
- from pydantic import Field
4
-
5
1
  from phylogenie.configs import StrictBaseModel
6
2
  from phylogenie.core.context.distributions import (
7
3
  DistributionConfig,
@@ -11,28 +7,22 @@ from phylogenie.core.context.distributions import (
11
7
 
12
8
  class VectorModel(StrictBaseModel):
13
9
  x: ScalarDistributionConfig
14
- N: int
15
10
 
16
11
 
17
12
  class Vector1DModel(VectorModel):
18
- n_dim: Literal[1] = 1
13
+ size: int
19
14
 
20
15
 
21
16
  class Vector2DModel(VectorModel):
22
- n_dim: Literal[2] = 2
17
+ size: tuple[int, int]
23
18
  zero_diagonal: bool = False
24
19
 
25
20
 
26
21
  class Vector3DModel(VectorModel):
27
- n_dim: Literal[3] = 3
28
- T: int
22
+ size: tuple[int, int, int]
29
23
  zero_diagonal: bool = False
30
24
 
31
25
 
32
26
  ContextConfig = dict[
33
- str,
34
- DistributionConfig
35
- | Annotated[
36
- Vector1DModel | Vector2DModel | Vector3DModel, Field(discriminator="n_dim")
37
- ],
27
+ str, DistributionConfig | Vector1DModel | Vector2DModel | Vector3DModel
38
28
  ]
@@ -77,7 +77,7 @@ class Exponential(Scalar):
77
77
  scale: float
78
78
 
79
79
  def _sample(self, rng: Generator) -> float:
80
- return rng.exponential(scale=self.scale)
80
+ return rng.exponential(self.scale)
81
81
 
82
82
 
83
83
  class Gamma(Scalar):
@@ -86,7 +86,7 @@ class Gamma(Scalar):
86
86
  shape: float
87
87
 
88
88
  def _sample(self, rng: Generator) -> float:
89
- return rng.gamma(shape=self.shape, scale=self.scale)
89
+ return rng.gamma(self.shape, self.scale)
90
90
 
91
91
 
92
92
  class Beta(Scalar):
@@ -95,7 +95,7 @@ class Beta(Scalar):
95
95
  beta: float
96
96
 
97
97
  def _sample(self, rng: Generator) -> float:
98
- return rng.beta(a=self.alpha, b=self.beta)
98
+ return rng.beta(self.alpha, self.beta)
99
99
 
100
100
 
101
101
  class IntUniform(Scalar):
@@ -0,0 +1,54 @@
1
+ from numpy.random import Generator
2
+
3
+ import phylogenie.core.context.configs as cfg
4
+ import phylogenie.typings as pgt
5
+ from phylogenie.core.context import distributions
6
+
7
+
8
+ def _sample_vector1D(x: distributions.Scalar, N: int, rng: Generator) -> pgt.Vector1D:
9
+ return [x.sample(rng) for _ in range(N)]
10
+
11
+
12
+ def _sample_vector2D(
13
+ x: distributions.Scalar,
14
+ size: tuple[int, int],
15
+ zero_diagonal: bool,
16
+ rng: Generator,
17
+ ) -> pgt.Vector2D:
18
+ n_rows, n_cols = size
19
+ v = [_sample_vector1D(x, n_cols, rng) for _ in range(n_rows)]
20
+ if zero_diagonal:
21
+ if n_rows != n_cols:
22
+ raise ValueError(
23
+ f"It is impossible to initialize a non-square matrix with zero the diagonal (got x={x}, size={size} and zero_diagonal=True)"
24
+ )
25
+ for i in range(n_rows):
26
+ v[i][i] = 0
27
+ return v
28
+
29
+
30
+ def _sample_vector3D(
31
+ x: distributions.Scalar,
32
+ size: tuple[int, int, int],
33
+ zero_diagonal: bool,
34
+ rng: Generator,
35
+ ) -> pgt.Vector3D:
36
+ n_matrices, n_rows, n_cols = size
37
+ return [
38
+ _sample_vector2D(x, (n_rows, n_cols), zero_diagonal, rng)
39
+ for _ in range(n_matrices)
40
+ ]
41
+
42
+
43
+ def context_factory(x: cfg.ContextConfig, rng: Generator) -> pgt.Data:
44
+ data: pgt.Data = {}
45
+ for key, value in x.items():
46
+ if isinstance(value, distributions.Distribution):
47
+ data[key] = value.sample(rng)
48
+ elif isinstance(value, cfg.Vector1DModel):
49
+ data[key] = _sample_vector1D(value.x, value.size, rng)
50
+ elif isinstance(value, cfg.Vector2DModel):
51
+ data[key] = _sample_vector2D(value.x, value.size, value.zero_diagonal, rng)
52
+ else:
53
+ data[key] = _sample_vector3D(value.x, value.size, value.zero_diagonal, rng)
54
+ return data
@@ -1,4 +1,4 @@
1
- from typing import Any, Literal, overload
1
+ from typing import Any
2
2
 
3
3
  import numpy as np
4
4
 
@@ -13,7 +13,6 @@ from phylogenie.skyline import (
13
13
  SkylineParameterLike,
14
14
  SkylineVector,
15
15
  SkylineVectorCoercible,
16
- SkylineVectorLike,
17
16
  )
18
17
 
19
18
 
@@ -100,17 +99,23 @@ def skyline_parameter_like_factory(
100
99
  )
101
100
 
102
101
 
103
- @overload
104
- def _parse_skyline_vector_value_model(
105
- x: cfg.SkylineVectorValueModel, data: pgt.Data, coercible: Literal[True]
106
- ) -> SkylineVectorCoercible: ...
107
- @overload
108
- def _parse_skyline_vector_value_model(
109
- x: cfg.SkylineVectorValueModel, data: pgt.Data, coercible: Literal[False]
110
- ) -> SkylineVectorLike: ...
111
- def _parse_skyline_vector_value_model(
112
- x: cfg.SkylineVectorValueModel, data: pgt.Data, coercible: bool
102
+ def skyline_vector_coercible_factory(
103
+ x: cfg.SkylineVectorCoercibleConfig, data: pgt.Data
113
104
  ) -> SkylineVectorCoercible:
105
+ if isinstance(x, str):
106
+ e = _eval_expression(x, data)
107
+ if tg.is_one_or_many_scalars(e):
108
+ return e
109
+ raise ValueError(
110
+ f"Expression '{x}' evaluated to {e} of type {type(e)}, expected a SkylineVectorCoercible object (e.g., a scalar or a sequence of them)."
111
+ )
112
+ if isinstance(x, pgt.Scalar):
113
+ return x
114
+ if ctg.is_list_of_skyline_parameter_like_configs(x):
115
+ return [skyline_parameter_like_factory(p, data) for p in x]
116
+
117
+ assert isinstance(x, cfg.SkylineVectorValueModel)
118
+
114
119
  change_times = many_scalars_factory(x.change_times, data)
115
120
  if isinstance(x.value, str):
116
121
  e = _eval_expression(x.value, data)
@@ -124,12 +129,7 @@ def _parse_skyline_vector_value_model(
124
129
  value = [one_or_many_scalars_factory(v, data) for v in x.value]
125
130
 
126
131
  if tg.is_many_scalars(value):
127
- if coercible:
128
- return SkylineParameter(value=value, change_times=change_times)
129
- else:
130
- raise ValueError(
131
- f"Parsing SkylineVector config {x.value} yielded a sequence of scalars {value} when a nested (2D) sequence of scalars was expected."
132
- )
132
+ return SkylineParameter(value=value, change_times=change_times)
133
133
 
134
134
  Ns = {len(elem) for elem in value if tg.is_many(elem)}
135
135
  if len(Ns) > 1:
@@ -139,46 +139,7 @@ def _parse_skyline_vector_value_model(
139
139
  (N,) = Ns
140
140
  value = [[p] * N if isinstance(p, pgt.Scalar) else p for p in value]
141
141
 
142
- return SkylineVector(
143
- value=value,
144
- change_times=change_times,
145
- )
146
-
147
-
148
- def skyline_vector_coercible_factory(
149
- x: cfg.SkylineVectorCoercibleConfig, data: pgt.Data
150
- ) -> SkylineVectorCoercible:
151
- if isinstance(x, str):
152
- e = _eval_expression(x, data)
153
- if tg.is_one_or_many_scalars(e):
154
- return e
155
- raise ValueError(
156
- f"Expression '{x}' evaluated to {e} of type {type(e)}, expected a SkylineVectorCoercible object (e.g., a scalar or a sequence of them)."
157
- )
158
- if isinstance(x, pgt.Scalar):
159
- return x
160
- if ctg.is_list_of_skyline_parameter_like_configs(x):
161
- return [skyline_parameter_like_factory(p, data) for p in x]
162
-
163
- assert isinstance(x, cfg.SkylineVectorValueModel)
164
- return _parse_skyline_vector_value_model(x, data, coercible=True)
165
-
166
-
167
- def skyline_vector_like_factory(
168
- x: cfg.SkylineVectorLikeConfig, data: pgt.Data
169
- ) -> SkylineVectorLike:
170
- if isinstance(x, str):
171
- e = _eval_expression(x, data)
172
- if tg.is_many_scalars(e):
173
- return e
174
- raise ValueError(
175
- f"Expression '{x}' evaluated to {e} of type {type(e)}, expected a SkylineVectorLike object (e.g., a sequence of scalars)."
176
- )
177
- if ctg.is_list_of_skyline_parameter_like_configs(x):
178
- return [skyline_parameter_like_factory(p, data) for p in x]
179
-
180
- assert isinstance(x, cfg.SkylineVectorValueModel)
181
- return _parse_skyline_vector_value_model(x, data, coercible=False)
142
+ return SkylineVector(value=value, change_times=change_times)
182
143
 
183
144
 
184
145
  def one_or_many_2D_scalars_factory(
@@ -208,8 +169,8 @@ def skyline_matrix_coercible_factory(
208
169
  )
209
170
  if isinstance(x, pgt.Scalar):
210
171
  return x
211
- if ctg.is_list_of_skyline_vector_like_configs(x):
212
- return [skyline_vector_like_factory(v, data) for v in x]
172
+ if ctg.is_list_of_skyline_vector_coercible_configs(x):
173
+ return [skyline_vector_coercible_factory(v, data) for v in x]
213
174
 
214
175
  assert isinstance(x, cfg.SkylineMatrixValueModel)
215
176
 
@@ -245,7 +206,4 @@ def skyline_matrix_coercible_factory(
245
206
  (N,) = Ns
246
207
  value = [[[p] * N] * N if isinstance(p, pgt.Scalar) else p for p in value]
247
208
 
248
- return SkylineMatrix(
249
- value=value,
250
- change_times=change_times,
251
- )
209
+ return SkylineMatrix(value=value, change_times=change_times)
@@ -0,0 +1,34 @@
1
+ import os
2
+ from abc import abstractmethod
3
+ from enum import Enum
4
+ from typing import Literal
5
+
6
+ from numpy.random import Generator
7
+
8
+ import phylogenie.typings as pgt
9
+ from phylogenie.core.dataset import DatasetGenerator, DataType
10
+ from phylogenie.core.trees import TreesGeneratorConfig
11
+
12
+
13
+ class BackendType(str, Enum):
14
+ ALISIM = "alisim"
15
+
16
+
17
+ class MSAsGenerator(DatasetGenerator):
18
+ data_type: Literal[DataType.MSAS] = DataType.MSAS
19
+ trees: TreesGeneratorConfig
20
+
21
+ @abstractmethod
22
+ def _generate_one_from_tree(
23
+ self, filename: str, tree_file: str, rng: Generator, data: pgt.Data
24
+ ) -> None: ...
25
+
26
+ def _generate_one(self, filename: str, rng: Generator, data: pgt.Data) -> None:
27
+ tree_filename = f"{filename}.temp-tree"
28
+ self.trees.generate_one(
29
+ filename=tree_filename, data=data, seed=int(rng.integers(0, 2**32 - 1))
30
+ )
31
+ self._generate_one_from_tree(
32
+ filename=filename, tree_file=f"{tree_filename}.nwk", rng=rng, data=data
33
+ )
34
+ os.remove(f"{tree_filename}.nwk")
@@ -67,9 +67,6 @@ class ReMASTERGenerator(TreesGenerator):
67
67
  beast_path=self.beast_path,
68
68
  )
69
69
 
70
- def _generate_one(self, filename: str, rng: Generator, data: pgt.Data) -> None:
71
- self._generate_one_from_extra_reactions(filename, rng, data, reactions=[])
72
-
73
70
 
74
71
  class CanonicalReMASTERGenerator(ReMASTERGenerator):
75
72
  parameterization: Literal[ParameterizationType.CANONICAL] = (
@@ -13,6 +13,7 @@ from phylogenie.backend.treesimulator import (
13
13
  generate_tree,
14
14
  get_BD_params,
15
15
  get_BDEI_params,
16
+ get_BDSS_params,
16
17
  )
17
18
  from phylogenie.core.factories import (
18
19
  int_factory,
@@ -25,9 +26,10 @@ from phylogenie.core.trees.base import BackendType, TreesGenerator
25
26
 
26
27
 
27
28
  class ParameterizationType(str, Enum):
28
- CANONICAL = "canonical"
29
+ MTBD = "MTBD"
29
30
  BD = "BD"
30
31
  BDEI = "BDEI"
32
+ BDSS = "BDSS"
31
33
 
32
34
 
33
35
  class TreeSimulatorGenerator(TreesGenerator):
@@ -70,15 +72,13 @@ class TreeSimulatorGenerator(TreesGenerator):
70
72
  )
71
73
 
72
74
 
73
- class CanonicalTreeSimulatorGenerator(TreeSimulatorGenerator):
74
- parameterization: Literal[ParameterizationType.CANONICAL] = (
75
- ParameterizationType.CANONICAL
76
- )
75
+ class MTBDTreeSimulatorGenerator(TreeSimulatorGenerator):
76
+ parameterization: Literal[ParameterizationType.MTBD] = ParameterizationType.MTBD
77
77
  populations: str | list[str] = DEFAULT_POPULATION
78
+ transmission_rates: cfg.SkylineMatrixCoercibleConfig
79
+ removal_rates: cfg.SkylineVectorCoercibleConfig
78
80
  transition_rates: cfg.SkylineMatrixCoercibleConfig = 0
79
- transmission_rates: cfg.SkylineMatrixCoercibleConfig = 0
80
- removal_rates: cfg.SkylineVectorCoercibleConfig = 0
81
- sampling_proportions: cfg.SkylineVectorCoercibleConfig = 0
81
+ sampling_proportions: cfg.SkylineVectorCoercibleConfig = 1
82
82
 
83
83
  def _generate_one(self, filename: str, rng: Generator, data: pgt.Data) -> None:
84
84
  self._generate_one_from_params(
@@ -105,9 +105,9 @@ class CanonicalTreeSimulatorGenerator(TreeSimulatorGenerator):
105
105
 
106
106
  class BDTreeSimulatorGenerator(TreeSimulatorGenerator):
107
107
  parameterization: Literal[ParameterizationType.BD] = ParameterizationType.BD
108
- reproduction_number: cfg.SkylineParameterLikeConfig = 0
109
- infectious_period: cfg.SkylineParameterLikeConfig = 0
110
- sampling_proportion: cfg.SkylineParameterLikeConfig = 0
108
+ reproduction_number: cfg.SkylineParameterLikeConfig
109
+ infectious_period: cfg.SkylineParameterLikeConfig
110
+ sampling_proportion: cfg.SkylineParameterLikeConfig = 1
111
111
 
112
112
  def _generate_one(self, filename: str, rng: Generator, data: pgt.Data) -> None:
113
113
  self._generate_one_from_params(
@@ -130,10 +130,10 @@ class BDTreeSimulatorGenerator(TreeSimulatorGenerator):
130
130
 
131
131
  class BDEITreeSimulatorGenerator(TreeSimulatorGenerator):
132
132
  parameterization: Literal[ParameterizationType.BDEI] = ParameterizationType.BDEI
133
- reproduction_number: cfg.SkylineParameterLikeConfig = 0
134
- infectious_period: cfg.SkylineParameterLikeConfig = 0
135
- incubation_period: cfg.SkylineParameterLikeConfig = 0
136
- sampling_proportion: cfg.SkylineParameterLikeConfig = 0
133
+ reproduction_number: cfg.SkylineParameterLikeConfig
134
+ infectious_period: cfg.SkylineParameterLikeConfig
135
+ incubation_period: cfg.SkylineParameterLikeConfig
136
+ sampling_proportion: cfg.SkylineParameterLikeConfig = 1
137
137
 
138
138
  def _generate_one(self, filename: str, rng: Generator, data: pgt.Data) -> None:
139
139
  self._generate_one_from_params(
@@ -157,9 +157,43 @@ class BDEITreeSimulatorGenerator(TreeSimulatorGenerator):
157
157
  )
158
158
 
159
159
 
160
+ class BDSSTreeSimulatorGenerator(TreeSimulatorGenerator):
161
+ parameterization: Literal[ParameterizationType.BDSS] = ParameterizationType.BDSS
162
+ reproduction_number: cfg.SkylineParameterLikeConfig
163
+ infectious_period: cfg.SkylineParameterLikeConfig
164
+ superspreading_ratio: cfg.SkylineParameterLikeConfig
165
+ superspreaders_proportion: cfg.SkylineParameterLikeConfig
166
+ sampling_proportion: cfg.SkylineParameterLikeConfig = 1
167
+
168
+ def _generate_one(self, filename: str, rng: Generator, data: pgt.Data) -> None:
169
+ self._generate_one_from_params(
170
+ filename,
171
+ rng,
172
+ data,
173
+ get_BDSS_params(
174
+ reproduction_number=skyline_parameter_like_factory(
175
+ self.reproduction_number, data
176
+ ),
177
+ infectious_period=skyline_parameter_like_factory(
178
+ self.infectious_period, data
179
+ ),
180
+ superspreading_ratio=skyline_parameter_like_factory(
181
+ self.superspreading_ratio, data
182
+ ),
183
+ superspreaders_proportion=skyline_parameter_like_factory(
184
+ self.superspreaders_proportion, data
185
+ ),
186
+ sampling_proportion=skyline_parameter_like_factory(
187
+ self.sampling_proportion, data
188
+ ),
189
+ ),
190
+ )
191
+
192
+
160
193
  TreeSimulatorGeneratorConfig = Annotated[
161
- CanonicalTreeSimulatorGenerator
194
+ MTBDTreeSimulatorGenerator
162
195
  | BDTreeSimulatorGenerator
163
- | BDEITreeSimulatorGenerator,
196
+ | BDEITreeSimulatorGenerator
197
+ | BDSSTreeSimulatorGenerator,
164
198
  Field(discriminator="parameterization"),
165
199
  ]
@@ -1,6 +1,7 @@
1
1
  from typing import TypeGuard
2
2
 
3
3
  import phylogenie.core.configs as cfg
4
+ import phylogenie.typings as pgt
4
5
 
5
6
 
6
7
  def is_list(x: object) -> TypeGuard[list[object]]:
@@ -17,11 +18,15 @@ def is_list_of_skyline_parameter_like_configs(
17
18
  return is_list(x) and all(isinstance(v, cfg.SkylineParameterLikeConfig) for v in x)
18
19
 
19
20
 
20
- def is_list_of_skyline_vector_like_configs(
21
+ def is_skyline_vector_coercible_config(
21
22
  x: object,
22
- ) -> TypeGuard[list[cfg.SkylineVectorLikeConfig]]:
23
- return is_list(x) and all(
24
- isinstance(v, str | cfg.SkylineVectorValueModel)
25
- or is_list_of_skyline_parameter_like_configs(v)
26
- for v in x
27
- )
23
+ ) -> TypeGuard[cfg.SkylineVectorCoercibleConfig]:
24
+ return isinstance(
25
+ x, str | pgt.Scalar | cfg.SkylineVectorValueModel
26
+ ) or is_list_of_skyline_parameter_like_configs(x)
27
+
28
+
29
+ def is_list_of_skyline_vector_coercible_configs(
30
+ x: object,
31
+ ) -> TypeGuard[list[cfg.SkylineVectorCoercibleConfig]]:
32
+ return is_list(x) and all(is_skyline_vector_coercible_config(v) for v in x)
@@ -1,28 +1,24 @@
1
1
  from collections.abc import Callable, Iterator
2
- from typing import TypeGuard, Union
2
+ from typing import TypeGuard, Union, overload
3
3
 
4
4
  import phylogenie.typeguards as tg
5
5
  import phylogenie.typings as pgt
6
- from phylogenie.skyline.parameter import (
7
- SkylineParameter,
8
- SkylineParameterLike,
9
- is_skyline_parameter_like,
10
- skyline_parameter,
11
- )
6
+ from phylogenie.skyline.parameter import SkylineParameter, is_skyline_parameter_like
12
7
  from phylogenie.skyline.vector import (
13
8
  SkylineVector,
9
+ SkylineVectorCoercible,
14
10
  SkylineVectorLike,
15
11
  SkylineVectorOperand,
12
+ is_many_skyline_vectors_coercible,
16
13
  is_many_skyline_vectors_like,
14
+ is_skyline_vector_coercible,
17
15
  is_skyline_vector_like,
18
16
  is_skyline_vector_operand,
19
17
  skyline_vector,
20
18
  )
21
19
 
22
20
  SkylineMatrixOperand = Union[SkylineVectorOperand, "SkylineMatrix"]
23
- SkylineMatrixCoercible = Union[
24
- SkylineParameterLike, pgt.Many[SkylineVectorLike], "SkylineMatrix"
25
- ]
21
+ SkylineMatrixCoercible = Union[pgt.OneOrMany[SkylineVectorCoercible], "SkylineMatrix"]
26
22
 
27
23
 
28
24
  def is_skyline_matrix_operand(x: object) -> TypeGuard[SkylineMatrixOperand]:
@@ -43,7 +39,7 @@ class SkylineMatrix:
43
39
  raise TypeError(
44
40
  f"It is impossible to create a SkylineMatrix from `params` {params} of type {type(params)}. Please provide a sequence composed of SkylineVectorLike objects (a SkylineVectorLike object can either be a SkylineVector or a sequence of scalars and/or SkylineParameters)."
45
41
  )
46
- elif value is not None and change_times is not None:
42
+ elif params is None and value is not None and change_times is not None:
47
43
  if tg.is_many_3D_scalars(value):
48
44
  matrix_lengths = {len(matrix) for matrix in value}
49
45
  if any(ml != len(value[0]) for ml in matrix_lengths):
@@ -87,11 +83,15 @@ class SkylineMatrix:
87
83
  [SkylineVector, SkylineVector | SkylineParameter], SkylineVector
88
84
  ],
89
85
  ) -> "SkylineMatrix":
90
- if not is_skyline_matrix_operand(other):
91
- return NotImplemented
92
86
  if is_skyline_vector_operand(other):
93
87
  other = skyline_vector(other, N=self.N)
94
- assert isinstance(other, SkylineVector | SkylineMatrix)
88
+ elif isinstance(other, SkylineMatrix):
89
+ if other.N != self.N:
90
+ raise ValueError(
91
+ f"Expected a SkylineMatrix with the same size as self (N={self.N}), but got {other} with N={other.N}."
92
+ )
93
+ else:
94
+ return NotImplemented
95
95
  return SkylineMatrix(
96
96
  [func(p1, p2) for p1, p2 in zip(self.params, other.params)]
97
97
  )
@@ -139,7 +139,13 @@ class SkylineMatrix:
139
139
  def __len__(self) -> int:
140
140
  return self.N
141
141
 
142
- def __getitem__(self, item: int) -> "SkylineVector":
142
+ @overload
143
+ def __getitem__(self, item: int) -> SkylineVector: ...
144
+ @overload
145
+ def __getitem__(self, item: slice) -> list[SkylineVector]: ...
146
+ def __getitem__(
147
+ self, item: int | slice
148
+ ) -> Union[SkylineVector, list[SkylineVector]]:
143
149
  return self.params[item]
144
150
 
145
151
  def __setitem__(self, item: int, value: SkylineVectorLike) -> None:
@@ -153,20 +159,36 @@ class SkylineMatrix:
153
159
  def skyline_matrix(
154
160
  x: SkylineMatrixCoercible, N: int, zero_diagonal: bool = False
155
161
  ) -> SkylineMatrix:
156
- if is_skyline_parameter_like(x):
157
- x = SkylineMatrix([[skyline_parameter(x)] * N] * N)
162
+ if N <= 0:
163
+ raise ValueError(
164
+ f"N must be a positive integer for SkylineMatrix construction (got N={N})."
165
+ )
166
+ if is_skyline_vector_coercible(x):
167
+ x = SkylineMatrix([[p] * N for p in skyline_vector(x, N)])
158
168
  if zero_diagonal:
159
169
  for i in range(N):
160
170
  x[i][i] = 0
161
171
  return x
162
- elif is_many_skyline_vectors_like(x):
163
- x = SkylineMatrix(x)
172
+ elif is_many_skyline_vectors_coercible(x):
173
+ x = SkylineMatrix(
174
+ [
175
+ [
176
+ (
177
+ 0
178
+ if i == j and is_skyline_parameter_like(v) and zero_diagonal
179
+ else p
180
+ )
181
+ for j, p in enumerate(skyline_vector(v, N))
182
+ ]
183
+ for i, v in enumerate(x)
184
+ ]
185
+ )
164
186
  if not isinstance(x, SkylineMatrix):
165
187
  raise TypeError(
166
188
  f"It is impossible to coerce {x} of type {type(x)} into a SkylineMatrix. Please provide either:\n"
167
189
  "- a SkylineMatrix,\n"
168
- "- a SkylineParameterLike object (i.e., a scalar or a SkylineParameter),\n"
169
- "- a sequence of SkylineVectorLike objects (a SkylineVectorLike object can be a SkylineVector or a sequence of SkylineParameterLike objects)."
190
+ "- a SkylineVectorCoercible object (i.e., a scalar, a SkylineParameter, a SkylineVector, or a sequence of scalars and/or SkylineParameters),\n"
191
+ "- a sequence of SkylineVectorCoercible objects."
170
192
  )
171
193
 
172
194
  if x.N != N:
@@ -69,7 +69,10 @@ class SkylineParameter:
69
69
  other: SkylineParameterLike,
70
70
  f: Callable[[pgt.Scalar, pgt.Scalar], pgt.Scalar],
71
71
  ) -> "SkylineParameter":
72
- other = skyline_parameter(other)
72
+ if is_skyline_parameter_like(other):
73
+ other = skyline_parameter(other)
74
+ else:
75
+ return NotImplemented
73
76
  change_times = sorted(set(self.change_times + other.change_times))
74
77
  value = [
75
78
  f(self.get_value_at_time(t), other.get_value_at_time(t))
@@ -1,5 +1,5 @@
1
1
  from collections.abc import Callable, Iterator
2
- from typing import TypeGuard, Union
2
+ from typing import TypeGuard, Union, overload
3
3
 
4
4
  import phylogenie.typeguards as tg
5
5
  import phylogenie.typings as pgt
@@ -24,10 +24,22 @@ def is_skyline_vector_like(x: object) -> TypeGuard[SkylineVectorLike]:
24
24
  return isinstance(x, SkylineVector) or is_many_skyline_parameters_like(x)
25
25
 
26
26
 
27
+ def is_skyline_vector_coercible(
28
+ x: object,
29
+ ) -> TypeGuard[SkylineVectorCoercible]:
30
+ return is_skyline_parameter_like(x) or is_skyline_vector_like(x)
31
+
32
+
27
33
  def is_many_skyline_vectors_like(x: object) -> TypeGuard[pgt.Many[SkylineVectorLike]]:
28
34
  return tg.is_many(x) and all(is_skyline_vector_like(v) for v in x)
29
35
 
30
36
 
37
+ def is_many_skyline_vectors_coercible(
38
+ x: object,
39
+ ) -> TypeGuard[pgt.Many[SkylineVectorCoercible]]:
40
+ return tg.is_many(x) and all(is_skyline_vector_coercible(v) for v in x)
41
+
42
+
31
43
  class SkylineVector:
32
44
  def __init__(
33
45
  self,
@@ -42,7 +54,7 @@ class SkylineVector:
42
54
  raise TypeError(
43
55
  f"It is impossible to create a SkylineVector from `params` {params} of type {type(params)}. Please provide a sequence of SkylineParameterLike objects (a SkylineParameterLike object can either be a SkylineParameter or a scalar)."
44
56
  )
45
- elif value is not None and change_times is not None:
57
+ elif params is None and value is not None and change_times is not None:
46
58
  if tg.is_many_2D_scalars(value):
47
59
  vector_lengths = {len(vector) for vector in value}
48
60
  if any(vl != len(value[0]) for vl in vector_lengths):
@@ -128,7 +140,13 @@ class SkylineVector:
128
140
  def __iter__(self) -> Iterator[SkylineParameter]:
129
141
  return iter(self.params)
130
142
 
131
- def __getitem__(self, item: int) -> "SkylineParameter":
143
+ @overload
144
+ def __getitem__(self, item: int) -> SkylineParameter: ...
145
+ @overload
146
+ def __getitem__(self, item: slice) -> list[SkylineParameter]: ...
147
+ def __getitem__(
148
+ self, item: int | slice
149
+ ) -> Union[SkylineParameter, list[SkylineParameter]]:
132
150
  return self.params[item]
133
151
 
134
152
  def __setitem__(self, item: int, value: SkylineParameterLike) -> None:
@@ -140,6 +158,10 @@ class SkylineVector:
140
158
 
141
159
 
142
160
  def skyline_vector(x: SkylineVectorCoercible, N: int) -> SkylineVector:
161
+ if N <= 0:
162
+ raise ValueError(
163
+ f"N must be a positive integer for SkylineVector construction (got N={N})."
164
+ )
143
165
  if is_skyline_parameter_like(x):
144
166
  return SkylineVector([skyline_parameter(x)] * N)
145
167
  elif is_many_skyline_parameters_like(x):
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "phylogenie"
3
- version = "1.0.4"
3
+ version = "1.0.6"
4
4
  description = "Generate phylogenetic datasets with minimal setup effort"
5
5
  authors = ["Gabriele Marino <gabmarino.8601@gmail.com>"]
6
6
  readme = "README.md"
@@ -1,41 +0,0 @@
1
- from numpy.random import Generator
2
-
3
- import phylogenie.core.context.configs as cfg
4
- import phylogenie.typings as pgt
5
- from phylogenie.core.context import distributions
6
-
7
-
8
- def _sample_vector1D(x: distributions.Scalar, N: int, rng: Generator) -> pgt.Vector1D:
9
- return [x.sample(rng) for _ in range(N)]
10
-
11
-
12
- def _sample_vector2D(
13
- x: distributions.Scalar, N: int, zero_diagonal: bool, rng: Generator
14
- ) -> pgt.Vector2D:
15
- v = [_sample_vector1D(x, N, rng) for _ in range(N)]
16
- if zero_diagonal:
17
- for i in range(N):
18
- v[i][i] = 0
19
- return v
20
-
21
-
22
- def _sample_vector3D(
23
- x: distributions.Scalar, N: int, T: int, zero_diagonal: bool, rng: Generator
24
- ) -> pgt.Vector3D:
25
- return [_sample_vector2D(x, N, zero_diagonal, rng) for _ in range(T)]
26
-
27
-
28
- def context_factory(x: cfg.ContextConfig, rng: Generator) -> pgt.Data:
29
- data: pgt.Data = {}
30
- for key, value in x.items():
31
- if isinstance(value, distributions.Distribution):
32
- data[key] = value.sample(rng)
33
- elif isinstance(value, cfg.Vector1DModel):
34
- data[key] = _sample_vector1D(value.x, value.N, rng)
35
- elif isinstance(value, cfg.Vector2DModel):
36
- data[key] = _sample_vector2D(value.x, value.N, value.zero_diagonal, rng)
37
- else:
38
- data[key] = _sample_vector3D(
39
- value.x, value.N, value.T, value.zero_diagonal, rng
40
- )
41
- return data
@@ -1,51 +0,0 @@
1
- import os
2
- from abc import abstractmethod
3
- from enum import Enum
4
- from pathlib import Path
5
- from typing import Literal
6
-
7
- from numpy.random import Generator
8
-
9
- import phylogenie.typings as pgt
10
- from phylogenie.core.dataset import DatasetGenerator, DataType
11
- from phylogenie.core.trees import TreesGeneratorConfig
12
-
13
-
14
- class BackendType(str, Enum):
15
- ALISIM = "alisim"
16
-
17
-
18
- class MSAsGenerator(DatasetGenerator):
19
- data_type: Literal[DataType.MSAS] = DataType.MSAS
20
- trees: str | TreesGeneratorConfig
21
- output_trees_dir: str | None = None
22
-
23
- @abstractmethod
24
- def _generate_one_from_tree(
25
- self, filename: str, tree_file: str, rng: Generator, data: pgt.Data
26
- ) -> None: ...
27
-
28
- def _generate_one(self, filename: str, rng: Generator, data: pgt.Data) -> None:
29
- if isinstance(self.trees, str):
30
- tree_files = os.listdir(self.trees)
31
- tree_file = os.path.join(self.trees, str(rng.choice(tree_files)))
32
- self._generate_one_from_tree(
33
- filename=filename, tree_file=tree_file, rng=rng, data=data
34
- )
35
- elif isinstance(self.output_trees_dir, str):
36
- os.makedirs(self.output_trees_dir, exist_ok=True)
37
- self._generate_one_from_tree(
38
- filename=filename,
39
- tree_file=os.path.join(self.output_trees_dir, f"{Path(filename).stem}"),
40
- rng=rng,
41
- data=data,
42
- )
43
- else:
44
- tree_filename = f"{filename}.temp-tree"
45
- self.trees.generate_one(
46
- filename=tree_filename, data=data, seed=int(rng.integers(0, 2**32 - 1))
47
- )
48
- self._generate_one_from_tree(
49
- filename=filename, tree_file=f"{tree_filename}.nwk", rng=rng, data=data
50
- )
51
- os.remove(f"{tree_filename}.nwk")
File without changes