phylogenie 1.0.1__py3-none-any.whl → 1.0.3__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.
@@ -11,7 +11,6 @@ from phylogenie.backend.remaster.reactions import (
11
11
  Reaction,
12
12
  )
13
13
  from phylogenie.skyline import skyline_parameter
14
- from phylogenie.utils import vectorify1D
15
14
 
16
15
  TREE_ID = "Tree"
17
16
 
@@ -75,12 +74,12 @@ def _generate_config_file(
75
74
  attrs = {
76
75
  "spec": "PunctualReaction",
77
76
  "value": punctual_reaction.value,
78
- "times": " ".join(map(str, vectorify1D(punctual_reaction.times))),
77
+ "times": " ".join(map(str, punctual_reaction.times)),
79
78
  }
80
79
  if punctual_reaction.p is not None:
81
- attrs["p"] = " ".join(map(str, vectorify1D(punctual_reaction.p)))
80
+ attrs["p"] = " ".join(map(str, punctual_reaction.p))
82
81
  if punctual_reaction.n is not None:
83
- attrs["n"] = " ".join(map(str, vectorify1D(punctual_reaction.n)))
82
+ attrs["n"] = " ".join(map(str, punctual_reaction.n))
84
83
  trajectory.append(Element("reaction", attrs))
85
84
 
86
85
  simulate.append(trajectory)
@@ -2,9 +2,9 @@ from dataclasses import dataclass
2
2
 
3
3
  import phylogenie.typings as pgt
4
4
  from phylogenie.skyline import (
5
- SkylineMatrixLike,
5
+ SkylineMatrixCoercible,
6
6
  SkylineParameterLike,
7
- SkylineVectorLike,
7
+ SkylineVectorCoercible,
8
8
  skyline_matrix,
9
9
  skyline_vector,
10
10
  )
@@ -21,21 +21,21 @@ class Reaction:
21
21
 
22
22
  @dataclass
23
23
  class PunctualReaction:
24
- times: pgt.OneOrManyScalars
24
+ times: pgt.ManyScalars
25
25
  value: str
26
- p: pgt.OneOrManyScalars | None = None
27
- n: pgt.OneOrMany[int] | None = None
26
+ p: pgt.ManyScalars | None = None
27
+ n: pgt.Many[int] | None = None
28
28
 
29
29
 
30
30
  def get_canonical_reactions(
31
31
  populations: str | list[str] = DEFAULT_POPULATION,
32
32
  sample_population: str = SAMPLE_POPULATION,
33
- birth_rates: SkylineVectorLike = 0,
34
- death_rates: SkylineVectorLike = 0,
35
- sampling_rates: SkylineVectorLike = 0,
36
- removal_probabilities: SkylineVectorLike = 0,
37
- migration_rates: SkylineMatrixLike = 0,
38
- birth_rates_among_demes: SkylineMatrixLike = 0,
33
+ birth_rates: SkylineVectorCoercible = 0,
34
+ death_rates: SkylineVectorCoercible = 0,
35
+ sampling_rates: SkylineVectorCoercible = 0,
36
+ removal_probabilities: SkylineVectorCoercible = 0,
37
+ migration_rates: SkylineMatrixCoercible = 0,
38
+ birth_rates_among_demes: SkylineMatrixCoercible = 0,
39
39
  ) -> list[Reaction]:
40
40
  if isinstance(populations, str):
41
41
  populations = [populations]
@@ -84,12 +84,12 @@ def get_canonical_reactions(
84
84
  def get_epidemiological_reactions(
85
85
  populations: str | list[str] = DEFAULT_POPULATION,
86
86
  sample_population: str = SAMPLE_POPULATION,
87
- reproduction_numbers: SkylineVectorLike = 0,
88
- become_uninfectious_rates: SkylineVectorLike = 0,
89
- sampling_proportions: SkylineVectorLike = 0,
90
- removal_probabilities: SkylineVectorLike = 0,
91
- migration_rates: SkylineMatrixLike = 0,
92
- reproduction_numbers_among_demes: SkylineMatrixLike = 0,
87
+ reproduction_numbers: SkylineVectorCoercible = 0,
88
+ become_uninfectious_rates: SkylineVectorCoercible = 0,
89
+ sampling_proportions: SkylineVectorCoercible = 0,
90
+ removal_probabilities: SkylineVectorCoercible = 0,
91
+ migration_rates: SkylineMatrixCoercible = 0,
92
+ reproduction_numbers_among_demes: SkylineMatrixCoercible = 0,
93
93
  ) -> list[Reaction]:
94
94
  if isinstance(populations, str):
95
95
  populations = [populations]
@@ -125,12 +125,12 @@ def get_epidemiological_reactions(
125
125
  def get_FBD_reactions(
126
126
  populations: str | list[str] = DEFAULT_POPULATION,
127
127
  sample_population: str = SAMPLE_POPULATION,
128
- diversification: SkylineVectorLike = 0,
129
- turnover: SkylineVectorLike = 0,
130
- sampling_proportions: SkylineVectorLike = 0,
131
- removal_probabilities: SkylineVectorLike = 0,
132
- migration_rates: SkylineMatrixLike = 0,
133
- diversification_between_types: SkylineMatrixLike = 0,
128
+ diversification: SkylineVectorCoercible = 0,
129
+ turnover: SkylineVectorCoercible = 0,
130
+ sampling_proportions: SkylineVectorCoercible = 0,
131
+ removal_probabilities: SkylineVectorCoercible = 0,
132
+ migration_rates: SkylineMatrixCoercible = 0,
133
+ diversification_between_types: SkylineMatrixCoercible = 0,
134
134
  ):
135
135
  if isinstance(populations, str):
136
136
  populations = [populations]
@@ -6,9 +6,9 @@ from treesimulator.generator import generate
6
6
  from treesimulator.mtbd_models import CTModel, Model
7
7
 
8
8
  from phylogenie.skyline import (
9
- SkylineMatrixLike,
9
+ SkylineMatrixCoercible,
10
10
  SkylineParameterLike,
11
- SkylineVectorLike,
11
+ SkylineVectorCoercible,
12
12
  skyline_matrix,
13
13
  skyline_parameter,
14
14
  skyline_vector,
@@ -22,10 +22,10 @@ EXPOSED_POPULATION = "E"
22
22
  @dataclass
23
23
  class TreeParams:
24
24
  populations: str | list[str] = DEFAULT_POPULATION
25
- transition_rates: SkylineMatrixLike = 0
26
- transmission_rates: SkylineMatrixLike = 0
27
- removal_rates: SkylineVectorLike = 0
28
- sampling_proportions: SkylineVectorLike = 0
25
+ transition_rates: SkylineMatrixCoercible = 0
26
+ transmission_rates: SkylineMatrixCoercible = 0
27
+ removal_rates: SkylineVectorCoercible = 0
28
+ sampling_proportions: SkylineVectorCoercible = 0
29
29
 
30
30
 
31
31
  def generate_tree(
@@ -3,33 +3,38 @@ from phylogenie.configs import StrictBaseModel
3
3
 
4
4
  IntConfig = str | int
5
5
  ScalarConfig = str | pgt.Scalar
6
- OneOrManyIntsConfig = str | int | pgt.Many[IntConfig]
7
- OneOrManyScalarsConfig = str | pgt.Scalar | pgt.Many[ScalarConfig]
6
+ ManyIntsConfig = str | list[IntConfig]
7
+ ManyScalarsConfig = str | list[ScalarConfig]
8
+ OneOrManyScalarsConfig = ScalarConfig | list[ScalarConfig]
9
+ OneOrMany2DScalarsConfig = ScalarConfig | list[list[ScalarConfig]]
8
10
 
9
11
 
10
12
  class SkylineParameterValueModel(StrictBaseModel):
11
- value: str | pgt.Many[ScalarConfig]
12
- change_times: OneOrManyScalarsConfig
13
+ value: ManyScalarsConfig
14
+ change_times: ManyScalarsConfig
13
15
 
14
16
 
15
- SkylineParameterLikeConfig = str | pgt.Scalar | SkylineParameterValueModel
17
+ SkylineParameterLikeConfig = ScalarConfig | SkylineParameterValueModel
16
18
 
17
19
 
18
20
  class SkylineVectorValueModel(StrictBaseModel):
19
- value: str | pgt.Many[OneOrManyScalarsConfig]
20
- change_times: OneOrManyScalarsConfig
21
+ value: str | list[OneOrManyScalarsConfig]
22
+ change_times: ManyScalarsConfig
21
23
 
22
24
 
25
+ SkylineVectorCoercibleConfig = (
26
+ str | pgt.Scalar | list[SkylineParameterLikeConfig] | SkylineVectorValueModel
27
+ )
23
28
  SkylineVectorLikeConfig = (
24
- str | pgt.Scalar | pgt.Many[SkylineParameterLikeConfig] | SkylineVectorValueModel
29
+ str | list[SkylineParameterLikeConfig] | SkylineVectorValueModel
25
30
  )
26
31
 
27
32
 
28
33
  class SkylineMatrixValueModel(StrictBaseModel):
29
- value: str | pgt.Many[pgt.OneOrMany2D[ScalarConfig]]
30
- change_times: OneOrManyScalarsConfig
34
+ value: str | list[OneOrMany2DScalarsConfig]
35
+ change_times: ManyScalarsConfig
31
36
 
32
37
 
33
- SkylineMatrixLikeConfig = (
34
- str | pgt.Scalar | pgt.Many[SkylineVectorLikeConfig] | SkylineMatrixValueModel
38
+ SkylineMatrixCoercibleConfig = (
39
+ str | pgt.Scalar | list[SkylineVectorLikeConfig] | SkylineMatrixValueModel
35
40
  )
@@ -3,18 +3,15 @@ from numpy.random import Generator
3
3
  import phylogenie.core.context.configs as cfg
4
4
  import phylogenie.typings as pgt
5
5
  from phylogenie.core.context import distributions
6
- from phylogenie.core.typings import Data
7
6
 
8
7
 
9
- def _sample_vector1D(
10
- x: distributions.Scalar, N: int, rng: Generator
11
- ) -> list[pgt.Scalar]:
8
+ def _sample_vector1D(x: distributions.Scalar, N: int, rng: Generator) -> pgt.Vector1D:
12
9
  return [x.sample(rng) for _ in range(N)]
13
10
 
14
11
 
15
12
  def _sample_vector2D(
16
13
  x: distributions.Scalar, N: int, zero_diagonal: bool, rng: Generator
17
- ) -> list[list[pgt.Scalar]]:
14
+ ) -> pgt.Vector2D:
18
15
  v = [_sample_vector1D(x, N, rng) for _ in range(N)]
19
16
  if zero_diagonal:
20
17
  for i in range(N):
@@ -24,12 +21,12 @@ def _sample_vector2D(
24
21
 
25
22
  def _sample_vector3D(
26
23
  x: distributions.Scalar, N: int, T: int, zero_diagonal: bool, rng: Generator
27
- ) -> list[list[list[pgt.Scalar]]]:
24
+ ) -> pgt.Vector3D:
28
25
  return [_sample_vector2D(x, N, zero_diagonal, rng) for _ in range(T)]
29
26
 
30
27
 
31
- def context_factory(x: cfg.ContextConfig, rng: Generator) -> Data:
32
- data: Data = {}
28
+ def context_factory(x: cfg.ContextConfig, rng: Generator) -> pgt.Data:
29
+ data: pgt.Data = {}
33
30
  for key, value in x.items():
34
31
  if isinstance(value, distributions.Distribution):
35
32
  data[key] = value.sample(rng)
@@ -7,9 +7,9 @@ import pandas as pd
7
7
  from numpy.random import Generator, default_rng
8
8
  from tqdm import tqdm
9
9
 
10
+ import phylogenie.typings as pgt
10
11
  from phylogenie.configs import StrictBaseModel
11
12
  from phylogenie.core.context import ContextConfig, context_factory
12
- from phylogenie.core.typings import Data
13
13
 
14
14
 
15
15
  class DataType(str, Enum):
@@ -27,10 +27,10 @@ class DatasetGenerator(ABC, StrictBaseModel):
27
27
  context: ContextConfig | None = None
28
28
 
29
29
  @abstractmethod
30
- def _generate_one(self, filename: str, rng: Generator, data: Data) -> None: ...
30
+ def _generate_one(self, filename: str, rng: Generator, data: pgt.Data) -> None: ...
31
31
 
32
32
  def generate_one(
33
- self, filename: str, data: Data | None = None, seed: int | None = None
33
+ self, filename: str, data: pgt.Data | None = None, seed: int | None = None
34
34
  ) -> None:
35
35
  data = {} if data is None else data
36
36
  self._generate_one(filename=filename, rng=default_rng(seed), data=data)
@@ -1,4 +1,4 @@
1
- from typing import Any
1
+ from typing import Any, Literal, overload
2
2
 
3
3
  import numpy as np
4
4
 
@@ -6,28 +6,31 @@ import phylogenie.core.configs as cfg
6
6
  import phylogenie.core.typeguards as ctg
7
7
  import phylogenie.typeguards as tg
8
8
  import phylogenie.typings as pgt
9
- from phylogenie.core.typings import Data
10
9
  from phylogenie.skyline import (
11
10
  SkylineMatrix,
12
- SkylineMatrixLike,
11
+ SkylineMatrixCoercible,
13
12
  SkylineParameter,
14
13
  SkylineParameterLike,
15
14
  SkylineVector,
15
+ SkylineVectorCoercible,
16
16
  SkylineVectorLike,
17
17
  )
18
18
 
19
19
 
20
- def _eval_expression(expression: str, data: Data) -> Any:
20
+ def _eval_expression(expression: str, data: pgt.Data) -> Any:
21
21
  return np.array(
22
22
  eval(
23
23
  expression,
24
- {"__builtins__": __builtins__},
25
- {k: np.array(v) for k, v in data.items()},
24
+ {
25
+ "__builtins__": __builtins__,
26
+ "np": np,
27
+ **{k: np.array(v) for k, v in data.items()},
28
+ },
26
29
  )
27
30
  ).tolist()
28
31
 
29
32
 
30
- def int_factory(x: cfg.IntConfig, data: Data) -> int:
33
+ def int_factory(x: cfg.IntConfig, data: pgt.Data) -> int:
31
34
  if isinstance(x, str):
32
35
  e = _eval_expression(x, data)
33
36
  if isinstance(e, int):
@@ -38,7 +41,7 @@ def int_factory(x: cfg.IntConfig, data: Data) -> int:
38
41
  return x
39
42
 
40
43
 
41
- def scalar_factory(x: cfg.ScalarConfig, data: Data) -> pgt.Scalar:
44
+ def scalar_factory(x: cfg.ScalarConfig, data: pgt.Data) -> pgt.Scalar:
42
45
  if isinstance(x, str):
43
46
  e = _eval_expression(x, data)
44
47
  if isinstance(e, pgt.Scalar):
@@ -49,130 +52,200 @@ def scalar_factory(x: cfg.ScalarConfig, data: Data) -> pgt.Scalar:
49
52
  return x
50
53
 
51
54
 
52
- def one_or_many_scalars_factory(
53
- x: cfg.OneOrManyScalarsConfig, data: Data
54
- ) -> pgt.OneOrMany[pgt.Scalar]:
55
+ def many_ints_factory(x: cfg.ManyIntsConfig, data: pgt.Data) -> pgt.Many[int]:
55
56
  if isinstance(x, str):
56
57
  e = _eval_expression(x, data)
57
- if tg.is_one_or_many_scalars(e):
58
+ if tg.is_many_ints(e):
58
59
  return e
59
60
  raise ValueError(
60
- f"Expression '{x}' evaluated to {e} of type {type(e)}, expected a scalar or a sequence of them."
61
+ f"Expression '{x}' evaluated to {e} of type {type(e)}, expected a sequence of integers."
61
62
  )
62
- if isinstance(x, pgt.Scalar):
63
- return x
64
- return [scalar_factory(v, data) for v in x]
63
+ return [int_factory(v, data) for v in x]
65
64
 
66
65
 
67
- def one_or_many_ints_factory(
68
- x: cfg.OneOrManyIntsConfig, data: Data
69
- ) -> pgt.OneOrMany[int]:
66
+ def many_scalars_factory(x: cfg.ManyScalarsConfig, data: pgt.Data) -> pgt.ManyScalars:
70
67
  if isinstance(x, str):
71
68
  e = _eval_expression(x, data)
72
- if tg.is_one_or_many_ints(e):
69
+ if tg.is_many_scalars(e):
73
70
  return e
74
71
  raise ValueError(
75
- f"Expression '{x}' evaluated to {e} of type {type(e)}, expected an int or a sequence of them."
72
+ f"Expression '{x}' evaluated to {e} of type {type(e)}, expected a sequence of scalars."
76
73
  )
77
- if isinstance(x, int):
78
- return x
79
- return [int_factory(v, data) for v in x]
74
+ return [scalar_factory(v, data) for v in x]
80
75
 
81
76
 
82
- def skyline_parameter_like_factory(
83
- x: cfg.SkylineParameterLikeConfig, data: Data
84
- ) -> SkylineParameterLike:
77
+ def one_or_many_scalars_factory(
78
+ x: cfg.OneOrManyScalarsConfig, data: pgt.Data
79
+ ) -> pgt.OneOrManyScalars:
85
80
  if isinstance(x, str):
86
81
  e = _eval_expression(x, data)
87
- if isinstance(e, pgt.Scalar):
82
+ if tg.is_one_or_many_scalars(e):
88
83
  return e
89
84
  raise ValueError(
90
- f"Expression '{x}' evaluated to {e} of type {type(e)}, expected a SkylineParameterLike (e.g., a scalar)."
85
+ f"Expression '{x}' evaluated to {e} of type {type(e)}, expected a scalar or a sequence of them."
91
86
  )
92
87
  if isinstance(x, pgt.Scalar):
93
88
  return x
89
+ return many_scalars_factory(x, data)
90
+
94
91
 
92
+ def skyline_parameter_like_factory(
93
+ x: cfg.SkylineParameterLikeConfig, data: pgt.Data
94
+ ) -> SkylineParameterLike:
95
+ if isinstance(x, cfg.ScalarConfig):
96
+ return scalar_factory(x, data)
97
+ return SkylineParameter(
98
+ value=many_scalars_factory(x.value, data),
99
+ change_times=many_scalars_factory(x.change_times, data),
100
+ )
101
+
102
+
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
113
+ ) -> SkylineVectorCoercible:
114
+ change_times = many_scalars_factory(x.change_times, data)
95
115
  if isinstance(x.value, str):
96
116
  e = _eval_expression(x.value, data)
97
- if tg.is_many_scalars(e):
117
+ if tg.is_many_one_or_many_scalars(e):
98
118
  value = e
99
119
  else:
100
120
  raise ValueError(
101
- f"Expression '{x.value}' evaluated to {e} of type {type(e)}, which is not a valid value for a SkylineParameter (expected a sequence of scalars)."
121
+ f"Expression '{x.value}' evaluated to {e} of type {type(e)}, which cannot be coerced to a valid value for a SkylineVector (expected a sequence composed of scalars and/or sequences of scalars)."
102
122
  )
103
123
  else:
104
- value = [scalar_factory(v, data) for v in x.value]
105
- return SkylineParameter(
124
+ value = [one_or_many_scalars_factory(v, data) for v in x.value]
125
+
126
+ 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
+ )
133
+
134
+ Ns = {len(elem) for elem in value if tg.is_many(elem)}
135
+ if len(Ns) > 1:
136
+ raise ValueError(
137
+ f"All elements in the value of a SkylineVector config must be scalars or have the same length (config {x.value} yielded value={value} with inconsistent lengths {Ns})."
138
+ )
139
+ (N,) = Ns
140
+ value = [[p] * N if isinstance(p, pgt.Scalar) else p for p in value]
141
+
142
+ return SkylineVector(
106
143
  value=value,
107
- change_times=one_or_many_scalars_factory(x.change_times, data),
144
+ change_times=change_times,
108
145
  )
109
146
 
110
147
 
111
- def skyline_vector_like_factory(
112
- x: cfg.SkylineVectorLikeConfig, data: Data
113
- ) -> SkylineVectorLike:
148
+ def skyline_vector_coercible_factory(
149
+ x: cfg.SkylineVectorCoercibleConfig, data: pgt.Data
150
+ ) -> SkylineVectorCoercible:
114
151
  if isinstance(x, str):
115
152
  e = _eval_expression(x, data)
116
153
  if tg.is_one_or_many_scalars(e):
117
154
  return e
118
155
  raise ValueError(
119
- f"Expression '{x}' evaluated to {e} of type {type(e)}, expected a SkylineVectorLike object (e.g., a scalar or a sequence of them)."
156
+ f"Expression '{x}' evaluated to {e} of type {type(e)}, expected a SkylineVectorCoercible object (e.g., a scalar or a sequence of them)."
120
157
  )
121
158
  if isinstance(x, pgt.Scalar):
122
159
  return x
123
- if ctg.is_many_skyline_parameter_like_configs(x):
160
+ if ctg.is_list_of_skyline_parameter_like_configs(x):
124
161
  return [skyline_parameter_like_factory(p, data) for p in x]
125
162
 
126
163
  assert isinstance(x, cfg.SkylineVectorValueModel)
127
- if isinstance(x.value, str):
128
- e = _eval_expression(x.value, data)
129
- if tg.is_many_one_or_many_scalars(e):
130
- value = e
131
- else:
132
- raise ValueError(
133
- f"Expression '{x.value}' evaluated to {e} of type {type(e)}, which is not a valid value for a SkylineVector (expected a sequence containing scalars and/or sequences of them)."
134
- )
135
- else:
136
- value = [one_or_many_scalars_factory(x, data) for x in x.value]
137
- return SkylineVector(
138
- value=value,
139
- change_times=one_or_many_scalars_factory(x.change_times, data),
140
- )
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]
141
179
 
180
+ assert isinstance(x, cfg.SkylineVectorValueModel)
181
+ return _parse_skyline_vector_value_model(x, data, coercible=False)
142
182
 
143
- def skyline_matrix_like_factory(
144
- x: cfg.SkylineMatrixLikeConfig, data: Data
145
- ) -> SkylineMatrixLike:
183
+
184
+ def one_or_many_2D_scalars_factory(
185
+ x: cfg.OneOrMany2DScalarsConfig, data: pgt.Data
186
+ ) -> pgt.OneOrMany2DScalars:
146
187
  if isinstance(x, str):
147
188
  e = _eval_expression(x, data)
148
- if tg.is_one_or_many_scalars(e) or tg.is_many_one_or_many_scalars(e):
189
+ if tg.is_one_or_many_2D_scalars(e):
149
190
  return e
150
191
  raise ValueError(
151
- f"Expression '{x}' evaluated to {e} of type {type(e)}, expected a SkylineMatrixLike object (e.g., a scalar, a sequence of them, or a sequence containing scalars and/or sequences of them)."
192
+ f"Expression '{x}' evaluated to {e} of type {type(e)}, expected a nested (2D) sequence of scalars."
152
193
  )
153
194
  if isinstance(x, pgt.Scalar):
154
195
  return x
155
- if ctg.is_many_skyline_vector_like_configs(x):
196
+ return [many_scalars_factory(v, data) for v in x]
197
+
198
+
199
+ def skyline_matrix_coercible_factory(
200
+ x: cfg.SkylineMatrixCoercibleConfig, data: pgt.Data
201
+ ) -> SkylineMatrixCoercible:
202
+ if isinstance(x, str):
203
+ e = _eval_expression(x, data)
204
+ if tg.is_one_or_many_2D_scalars(e):
205
+ return e
206
+ raise ValueError(
207
+ f"Expression '{x}' evaluated to {e} of type {type(e)}, expected a SkylineMatrixCoercible object (e.g., a scalar or a nested (2D) sequence of them)."
208
+ )
209
+ if isinstance(x, pgt.Scalar):
210
+ return x
211
+ if ctg.is_list_of_skyline_vector_like_configs(x):
156
212
  return [skyline_vector_like_factory(v, data) for v in x]
157
213
 
158
214
  assert isinstance(x, cfg.SkylineMatrixValueModel)
215
+
216
+ change_times = many_scalars_factory(x.change_times, data)
159
217
  if isinstance(x.value, str):
160
218
  e = _eval_expression(x.value, data)
161
- if tg.is_many_one_or_many_2d_scalars(e):
219
+ if tg.is_many_one_or_many_2D_scalars(e):
162
220
  value = e
163
221
  else:
164
222
  raise ValueError(
165
- f"Expression '{x.value}' evaluated to {e} of type {type(e)}, which is not a valid value for a SkylineMatrix (expected a sequence containing scalars and/or nested (2D) sequences of them)."
223
+ f"Expression '{x.value}' evaluated to {e} of type {type(e)}, which cannot be coerced to a valid value for a SkylineMatrix (expected a sequence composed of scalars and/or nested (2D) sequences of scalars)."
166
224
  )
167
225
  else:
168
- value = [
169
- (
170
- scalar_factory(v, data)
171
- if isinstance(v, cfg.ScalarConfig)
172
- else [[scalar_factory(row, data) for row in m] for m in v]
173
- )
174
- for v in x.value
175
- ]
226
+ value = [one_or_many_2D_scalars_factory(v, data) for v in x.value]
227
+
228
+ if tg.is_many_scalars(value):
229
+ return SkylineParameter(value=value, change_times=change_times)
230
+
231
+ Ns: set[int] = set()
232
+ for elem in value:
233
+ if tg.is_many_2D_scalars(elem):
234
+ n_rows = len(elem)
235
+ if any(len(row) != n_rows for row in elem):
236
+ raise ValueError(
237
+ f"All elements in the value of a SkylineMatrix config must be scalars or square matrices (config {x.value} yeilded a non-square matrix: {elem})."
238
+ )
239
+ Ns.add(n_rows)
240
+
241
+ if len(Ns) > 1:
242
+ raise ValueError(
243
+ f"All elements in the value of a SkylineMatrix config must be scalars or have the same square shape (config {x.value} yielded value={value} with inconsistent lengths {Ns})."
244
+ )
245
+ (N,) = Ns
246
+ value = [[[p] * N] * N if isinstance(p, pgt.Scalar) else p for p in value]
247
+
176
248
  return SkylineMatrix(
177
- value=value, change_times=one_or_many_scalars_factory(x.change_times, data)
249
+ value=value,
250
+ change_times=change_times,
178
251
  )
@@ -3,8 +3,8 @@ from typing import Literal
3
3
 
4
4
  from numpy.random import Generator
5
5
 
6
+ import phylogenie.typings as pgt
6
7
  from phylogenie.core.msas.base import BackendType, MSAsGenerator
7
- from phylogenie.core.typings import Data
8
8
 
9
9
 
10
10
  class AliSimGenerator(MSAsGenerator):
@@ -13,7 +13,7 @@ class AliSimGenerator(MSAsGenerator):
13
13
  args: dict[str, str | int | float]
14
14
 
15
15
  def _generate_one_from_tree(
16
- self, filename: str, tree_file: str, rng: Generator, data: Data
16
+ self, filename: str, tree_file: str, rng: Generator, data: pgt.Data
17
17
  ) -> None:
18
18
  command = [
19
19
  self.iqtree_path,
@@ -6,9 +6,9 @@ from typing import Literal
6
6
 
7
7
  from numpy.random import Generator
8
8
 
9
+ import phylogenie.typings as pgt
9
10
  from phylogenie.core.dataset import DatasetGenerator, DataType
10
11
  from phylogenie.core.trees import TreesGeneratorConfig
11
- from phylogenie.core.typings import Data
12
12
 
13
13
 
14
14
  class BackendType(str, Enum):
@@ -22,10 +22,10 @@ class MSAsGenerator(DatasetGenerator):
22
22
 
23
23
  @abstractmethod
24
24
  def _generate_one_from_tree(
25
- self, filename: str, tree_file: str, rng: Generator, data: Data
25
+ self, filename: str, tree_file: str, rng: Generator, data: pgt.Data
26
26
  ) -> None: ...
27
27
 
28
- def _generate_one(self, filename: str, rng: Generator, data: Data) -> None:
28
+ def _generate_one(self, filename: str, rng: Generator, data: pgt.Data) -> None:
29
29
  if isinstance(self.trees, str):
30
30
  tree_files = os.listdir(self.trees)
31
31
  tree_file = os.path.join(self.trees, str(rng.choice(tree_files)))
@@ -8,7 +8,7 @@ class ReactionConfig(StrictBaseModel):
8
8
 
9
9
 
10
10
  class PunctualReactionConfig(StrictBaseModel):
11
- times: cfg.OneOrManyScalarsConfig
11
+ times: cfg.ManyScalarsConfig
12
12
  value: str
13
- p: cfg.OneOrManyScalarsConfig | None = None
14
- n: cfg.OneOrManyIntsConfig | None = None
13
+ p: cfg.ManyScalarsConfig | None = None
14
+ n: cfg.ManyIntsConfig | None = None
@@ -1,17 +1,14 @@
1
1
  import phylogenie.core.trees.remaster.configs as cfg
2
+ import phylogenie.typings as pgt
2
3
  from phylogenie.backend.remaster import PunctualReaction, Reaction
3
4
  from phylogenie.core.factories import (
4
- one_or_many_ints_factory,
5
- one_or_many_scalars_factory,
5
+ many_ints_factory,
6
+ many_scalars_factory,
6
7
  skyline_parameter_like_factory,
7
8
  )
8
- from phylogenie.core.typings import Data
9
9
 
10
10
 
11
- def reaction_factory(
12
- x: cfg.ReactionConfig,
13
- data: Data,
14
- ) -> Reaction:
11
+ def reaction_factory(x: cfg.ReactionConfig, data: pgt.Data) -> Reaction:
15
12
  return Reaction(
16
13
  rate=skyline_parameter_like_factory(x.rate, data),
17
14
  value=x.value,
@@ -19,12 +16,11 @@ def reaction_factory(
19
16
 
20
17
 
21
18
  def punctual_reaction_factory(
22
- x: cfg.PunctualReactionConfig,
23
- data: Data,
19
+ x: cfg.PunctualReactionConfig, data: pgt.Data
24
20
  ) -> PunctualReaction:
25
21
  return PunctualReaction(
26
- times=one_or_many_scalars_factory(x.times, data),
22
+ times=many_scalars_factory(x.times, data),
27
23
  value=x.value,
28
- p=None if x.p is None else one_or_many_scalars_factory(x.p, data),
29
- n=None if x.n is None else one_or_many_ints_factory(x.n, data),
24
+ p=None if x.p is None else many_scalars_factory(x.p, data),
25
+ n=None if x.n is None else many_ints_factory(x.n, data),
30
26
  )