phylogenie 1.0.0__py3-none-any.whl → 1.0.2__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.
@@ -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
  )
@@ -6,6 +6,7 @@ from numpy.random import Generator
6
6
  from pydantic import Field
7
7
 
8
8
  import phylogenie.core.configs as cfg
9
+ import phylogenie.typings as pgt
9
10
  from phylogenie.backend.remaster import (
10
11
  DEFAULT_POPULATION,
11
12
  SAMPLE_POPULATION,
@@ -16,8 +17,8 @@ from phylogenie.backend.remaster import (
16
17
  get_FBD_reactions,
17
18
  )
18
19
  from phylogenie.core.factories import (
19
- skyline_matrix_like_factory,
20
- skyline_vector_like_factory,
20
+ skyline_matrix_coercible_factory,
21
+ skyline_vector_coercible_factory,
21
22
  )
22
23
  from phylogenie.core.trees.base import BackendType, TreesGenerator
23
24
  from phylogenie.core.trees.remaster.configs import (
@@ -28,7 +29,6 @@ from phylogenie.core.trees.remaster.factories import (
28
29
  punctual_reaction_factory,
29
30
  reaction_factory,
30
31
  )
31
- from phylogenie.core.typings import Data
32
32
 
33
33
 
34
34
  class ParameterizationType(str, Enum):
@@ -39,6 +39,7 @@ class ParameterizationType(str, Enum):
39
39
 
40
40
  class ReMASTERGenerator(TreesGenerator):
41
41
  backend: Literal[BackendType.REMASTER] = BackendType.REMASTER
42
+ beast_path: str = "beast"
42
43
  populations: str | list[str] = DEFAULT_POPULATION
43
44
  init_population: str = DEFAULT_POPULATION
44
45
  sample_population: str = SAMPLE_POPULATION
@@ -47,7 +48,7 @@ class ReMASTERGenerator(TreesGenerator):
47
48
  trajectory_attrs: dict[str, str | int | float] = Field(default_factory=dict)
48
49
 
49
50
  def _generate_one_from_extra_reactions(
50
- self, filename: str, rng: Generator, data: Data, reactions: list[Reaction]
51
+ self, filename: str, rng: Generator, data: pgt.Data, reactions: list[Reaction]
51
52
  ) -> None:
52
53
  generate_trees(
53
54
  tree_file_name=f"{filename}.nwk",
@@ -63,9 +64,10 @@ class ReMASTERGenerator(TreesGenerator):
63
64
  for k, v in self.trajectory_attrs.items()
64
65
  },
65
66
  seed=int(rng.integers(0, 2**31 - 1)),
67
+ beast_path=self.beast_path,
66
68
  )
67
69
 
68
- def _generate_one(self, filename: str, rng: Generator, data: Data) -> None:
70
+ def _generate_one(self, filename: str, rng: Generator, data: pgt.Data) -> None:
69
71
  self._generate_one_from_extra_reactions(filename, rng, data, reactions=[])
70
72
 
71
73
 
@@ -73,25 +75,27 @@ class CanonicalReMASTERGenerator(ReMASTERGenerator):
73
75
  parameterization: Literal[ParameterizationType.CANONICAL] = (
74
76
  ParameterizationType.CANONICAL
75
77
  )
76
- birth_rates: cfg.SkylineVectorLikeConfig = 0
77
- death_rates: cfg.SkylineVectorLikeConfig = 0
78
- sampling_rates: cfg.SkylineVectorLikeConfig = 0
79
- removal_probabilities: cfg.SkylineVectorLikeConfig = 0
80
- migration_rates: cfg.SkylineMatrixLikeConfig = 0
81
- birth_rates_among_demes: cfg.SkylineMatrixLikeConfig = 0
82
-
83
- def _generate_one(self, filename: str, rng: Generator, data: Data) -> None:
78
+ birth_rates: cfg.SkylineVectorCoercibleConfig = 0
79
+ death_rates: cfg.SkylineVectorCoercibleConfig = 0
80
+ sampling_rates: cfg.SkylineVectorCoercibleConfig = 0
81
+ removal_probabilities: cfg.SkylineVectorCoercibleConfig = 0
82
+ migration_rates: cfg.SkylineMatrixCoercibleConfig = 0
83
+ birth_rates_among_demes: cfg.SkylineMatrixCoercibleConfig = 0
84
+
85
+ def _generate_one(self, filename: str, rng: Generator, data: pgt.Data) -> None:
84
86
  reactions = get_canonical_reactions(
85
87
  populations=self.populations,
86
88
  sample_population=self.sample_population,
87
- birth_rates=skyline_vector_like_factory(self.birth_rates, data),
88
- death_rates=skyline_vector_like_factory(self.death_rates, data),
89
- sampling_rates=skyline_vector_like_factory(self.sampling_rates, data),
90
- removal_probabilities=skyline_vector_like_factory(
89
+ birth_rates=skyline_vector_coercible_factory(self.birth_rates, data),
90
+ death_rates=skyline_vector_coercible_factory(self.death_rates, data),
91
+ sampling_rates=skyline_vector_coercible_factory(self.sampling_rates, data),
92
+ removal_probabilities=skyline_vector_coercible_factory(
91
93
  self.removal_probabilities, data
92
94
  ),
93
- migration_rates=skyline_matrix_like_factory(self.migration_rates, data),
94
- birth_rates_among_demes=skyline_matrix_like_factory(
95
+ migration_rates=skyline_matrix_coercible_factory(
96
+ self.migration_rates, data
97
+ ),
98
+ birth_rates_among_demes=skyline_matrix_coercible_factory(
95
99
  self.birth_rates_among_demes, data
96
100
  ),
97
101
  )
@@ -102,31 +106,33 @@ class EpidemiologicalReMASTERGenerator(ReMASTERGenerator):
102
106
  parameterization: Literal[ParameterizationType.EPIDEMIOLOGICAL] = (
103
107
  ParameterizationType.EPIDEMIOLOGICAL
104
108
  )
105
- reproduction_numbers: cfg.SkylineVectorLikeConfig = 0
106
- become_uninfectious_rates: cfg.SkylineVectorLikeConfig = 0
107
- sampling_proportions: cfg.SkylineVectorLikeConfig = 0
108
- removal_probabilities: cfg.SkylineVectorLikeConfig = 0
109
- migration_rates: cfg.SkylineMatrixLikeConfig = 0
110
- reproduction_numbers_among_demes: cfg.SkylineMatrixLikeConfig = 0
111
-
112
- def _generate_one(self, filename: str, rng: Generator, data: Data) -> None:
109
+ reproduction_numbers: cfg.SkylineVectorCoercibleConfig = 0
110
+ become_uninfectious_rates: cfg.SkylineVectorCoercibleConfig = 0
111
+ sampling_proportions: cfg.SkylineVectorCoercibleConfig = 0
112
+ removal_probabilities: cfg.SkylineVectorCoercibleConfig = 0
113
+ migration_rates: cfg.SkylineMatrixCoercibleConfig = 0
114
+ reproduction_numbers_among_demes: cfg.SkylineMatrixCoercibleConfig = 0
115
+
116
+ def _generate_one(self, filename: str, rng: Generator, data: pgt.Data) -> None:
113
117
  reactions = get_epidemiological_reactions(
114
118
  populations=self.populations,
115
119
  sample_population=self.sample_population,
116
- reproduction_numbers=skyline_vector_like_factory(
120
+ reproduction_numbers=skyline_vector_coercible_factory(
117
121
  self.reproduction_numbers, data
118
122
  ),
119
- become_uninfectious_rates=skyline_vector_like_factory(
123
+ become_uninfectious_rates=skyline_vector_coercible_factory(
120
124
  self.become_uninfectious_rates, data
121
125
  ),
122
- sampling_proportions=skyline_vector_like_factory(
126
+ sampling_proportions=skyline_vector_coercible_factory(
123
127
  self.sampling_proportions, data
124
128
  ),
125
- removal_probabilities=skyline_vector_like_factory(
129
+ removal_probabilities=skyline_vector_coercible_factory(
126
130
  self.removal_probabilities, data
127
131
  ),
128
- migration_rates=skyline_matrix_like_factory(self.migration_rates, data),
129
- reproduction_numbers_among_demes=skyline_matrix_like_factory(
132
+ migration_rates=skyline_matrix_coercible_factory(
133
+ self.migration_rates, data
134
+ ),
135
+ reproduction_numbers_among_demes=skyline_matrix_coercible_factory(
130
136
  self.reproduction_numbers_among_demes, data
131
137
  ),
132
138
  )
@@ -135,27 +141,31 @@ class EpidemiologicalReMASTERGenerator(ReMASTERGenerator):
135
141
 
136
142
  class FBDReMASTERGenerator(ReMASTERGenerator):
137
143
  parameterization: Literal[ParameterizationType.FBD] = ParameterizationType.FBD
138
- diversification: cfg.SkylineVectorLikeConfig = 0
139
- turnover: cfg.SkylineVectorLikeConfig = 0
140
- sampling_proportions: cfg.SkylineVectorLikeConfig = 0
141
- removal_probabilities: cfg.SkylineVectorLikeConfig = 0
142
- migration_rates: cfg.SkylineMatrixLikeConfig = 0
143
- diversification_between_types: cfg.SkylineMatrixLikeConfig = 0
144
-
145
- def _generate_one(self, filename: str, rng: Generator, data: Data) -> None:
144
+ diversification: cfg.SkylineVectorCoercibleConfig = 0
145
+ turnover: cfg.SkylineVectorCoercibleConfig = 0
146
+ sampling_proportions: cfg.SkylineVectorCoercibleConfig = 0
147
+ removal_probabilities: cfg.SkylineVectorCoercibleConfig = 0
148
+ migration_rates: cfg.SkylineMatrixCoercibleConfig = 0
149
+ diversification_between_types: cfg.SkylineMatrixCoercibleConfig = 0
150
+
151
+ def _generate_one(self, filename: str, rng: Generator, data: pgt.Data) -> None:
146
152
  reactions = get_FBD_reactions(
147
153
  populations=self.populations,
148
154
  sample_population=self.sample_population,
149
- diversification=skyline_vector_like_factory(self.diversification, data),
150
- turnover=skyline_vector_like_factory(self.turnover, data),
151
- sampling_proportions=skyline_vector_like_factory(
155
+ diversification=skyline_vector_coercible_factory(
156
+ self.diversification, data
157
+ ),
158
+ turnover=skyline_vector_coercible_factory(self.turnover, data),
159
+ sampling_proportions=skyline_vector_coercible_factory(
152
160
  self.sampling_proportions, data
153
161
  ),
154
- removal_probabilities=skyline_vector_like_factory(
162
+ removal_probabilities=skyline_vector_coercible_factory(
155
163
  self.removal_probabilities, data
156
164
  ),
157
- migration_rates=skyline_matrix_like_factory(self.migration_rates, data),
158
- diversification_between_types=skyline_matrix_like_factory(
165
+ migration_rates=skyline_matrix_coercible_factory(
166
+ self.migration_rates, data
167
+ ),
168
+ diversification_between_types=skyline_matrix_coercible_factory(
159
169
  self.diversification_between_types, data
160
170
  ),
161
171
  )
@@ -6,6 +6,7 @@ from numpy.random import Generator
6
6
  from pydantic import Field
7
7
 
8
8
  import phylogenie.core.configs as cfg
9
+ import phylogenie.typings as pgt
9
10
  from phylogenie.backend.treesimulator import (
10
11
  DEFAULT_POPULATION,
11
12
  TreeParams,
@@ -16,12 +17,11 @@ from phylogenie.backend.treesimulator import (
16
17
  from phylogenie.core.factories import (
17
18
  int_factory,
18
19
  scalar_factory,
19
- skyline_matrix_like_factory,
20
+ skyline_matrix_coercible_factory,
20
21
  skyline_parameter_like_factory,
21
- skyline_vector_like_factory,
22
+ skyline_vector_coercible_factory,
22
23
  )
23
24
  from phylogenie.core.trees.base import BackendType, TreesGenerator
24
- from phylogenie.core.typings import Data
25
25
 
26
26
 
27
27
  class ParameterizationType(str, Enum):
@@ -35,15 +35,15 @@ class TreeSimulatorGenerator(TreesGenerator):
35
35
  min_tips: cfg.IntConfig
36
36
  max_tips: cfg.IntConfig
37
37
  T: cfg.ScalarConfig = np.inf
38
+ root_state: str | None = None
38
39
  state_frequencies: list[float] | None = None
39
40
  notification_probability: cfg.SkylineParameterLikeConfig = 0
40
41
  notification_sampling_rate: cfg.SkylineParameterLikeConfig = np.inf
41
42
  allow_irremovable_states: bool = False
42
43
  max_notified_contacts: cfg.IntConfig = 1
43
- root_state: str | None = None
44
44
 
45
45
  def _generate_one_from_params(
46
- self, filename: str, rng: Generator, data: Data, params: TreeParams
46
+ self, filename: str, rng: Generator, data: pgt.Data, params: TreeParams
47
47
  ) -> None:
48
48
  root_state = (
49
49
  self.root_state
@@ -75,26 +75,28 @@ class CanonicalTreeSimulatorGenerator(TreeSimulatorGenerator):
75
75
  ParameterizationType.CANONICAL
76
76
  )
77
77
  populations: str | list[str] = DEFAULT_POPULATION
78
- transition_rates: cfg.SkylineMatrixLikeConfig = 0
79
- transmission_rates: cfg.SkylineMatrixLikeConfig = 0
80
- removal_rates: cfg.SkylineVectorLikeConfig = 0
81
- sampling_proportions: cfg.SkylineVectorLikeConfig = 0
78
+ transition_rates: cfg.SkylineMatrixCoercibleConfig = 0
79
+ transmission_rates: cfg.SkylineMatrixCoercibleConfig = 0
80
+ removal_rates: cfg.SkylineVectorCoercibleConfig = 0
81
+ sampling_proportions: cfg.SkylineVectorCoercibleConfig = 0
82
82
 
83
- def _generate_one(self, filename: str, rng: Generator, data: Data) -> None:
83
+ def _generate_one(self, filename: str, rng: Generator, data: pgt.Data) -> None:
84
84
  self._generate_one_from_params(
85
85
  filename,
86
86
  rng,
87
87
  data,
88
88
  TreeParams(
89
89
  populations=self.populations,
90
- transition_rates=skyline_matrix_like_factory(
90
+ transition_rates=skyline_matrix_coercible_factory(
91
91
  self.transition_rates, data
92
92
  ),
93
- transmission_rates=skyline_matrix_like_factory(
93
+ transmission_rates=skyline_matrix_coercible_factory(
94
94
  self.transmission_rates, data
95
95
  ),
96
- removal_rates=skyline_vector_like_factory(self.removal_rates, data),
97
- sampling_proportions=skyline_vector_like_factory(
96
+ removal_rates=skyline_vector_coercible_factory(
97
+ self.removal_rates, data
98
+ ),
99
+ sampling_proportions=skyline_vector_coercible_factory(
98
100
  self.sampling_proportions, data
99
101
  ),
100
102
  ),
@@ -107,7 +109,7 @@ class BDTreeSimulatorGenerator(TreeSimulatorGenerator):
107
109
  infectious_period: cfg.SkylineParameterLikeConfig = 0
108
110
  sampling_proportion: cfg.SkylineParameterLikeConfig = 0
109
111
 
110
- def _generate_one(self, filename: str, rng: Generator, data: Data) -> None:
112
+ def _generate_one(self, filename: str, rng: Generator, data: pgt.Data) -> None:
111
113
  self._generate_one_from_params(
112
114
  filename,
113
115
  rng,
@@ -133,7 +135,7 @@ class BDEITreeSimulatorGenerator(TreeSimulatorGenerator):
133
135
  incubation_period: cfg.SkylineParameterLikeConfig = 0
134
136
  sampling_proportion: cfg.SkylineParameterLikeConfig = 0
135
137
 
136
- def _generate_one(self, filename: str, rng: Generator, data: Data) -> None:
138
+ def _generate_one(self, filename: str, rng: Generator, data: pgt.Data) -> None:
137
139
  self._generate_one_from_params(
138
140
  filename,
139
141
  rng,
@@ -1,35 +1,27 @@
1
1
  from typing import TypeGuard
2
2
 
3
3
  import phylogenie.core.configs as cfg
4
- import phylogenie.typeguards as tg
5
- import phylogenie.typings as pgt
6
4
 
7
5
 
8
- def is_many_scalar_configs(x: object) -> TypeGuard[pgt.Many[cfg.ScalarConfig]]:
9
- return tg.is_many(x) and all(isinstance(v, cfg.ScalarConfig) for v in x)
6
+ def is_list(x: object) -> TypeGuard[list[object]]:
7
+ return isinstance(x, list)
10
8
 
11
9
 
12
- def is_many_2D_scalar_configs(x: object) -> TypeGuard[pgt.Many2D[cfg.ScalarConfig]]:
13
- return tg.is_many(x) and all(is_many_scalar_configs(v) for v in x)
10
+ def is_list_of_scalar_configs(x: object) -> TypeGuard[list[cfg.ScalarConfig]]:
11
+ return is_list(x) and all(isinstance(v, cfg.ScalarConfig) for v in x)
14
12
 
15
13
 
16
- def is_many_3D_scalar_configs(x: object) -> TypeGuard[pgt.Many3D[cfg.ScalarConfig]]:
17
- return tg.is_many(x) and all(is_many_2D_scalar_configs(v) for v in x)
18
-
19
-
20
- def is_many_skyline_parameter_like_configs(
14
+ def is_list_of_skyline_parameter_like_configs(
21
15
  x: object,
22
- ) -> TypeGuard[pgt.Many[cfg.SkylineParameterLikeConfig]]:
23
- return tg.is_many(x) and all(
24
- isinstance(v, cfg.SkylineParameterLikeConfig) for v in x
25
- )
16
+ ) -> TypeGuard[list[cfg.SkylineParameterLikeConfig]]:
17
+ return is_list(x) and all(isinstance(v, cfg.SkylineParameterLikeConfig) for v in x)
26
18
 
27
19
 
28
- def is_many_skyline_vector_like_configs(
20
+ def is_list_of_skyline_vector_like_configs(
29
21
  x: object,
30
- ) -> TypeGuard[pgt.Many[cfg.SkylineVectorLikeConfig]]:
31
- return tg.is_many(x) and all(
32
- isinstance(v, str | pgt.Scalar | cfg.SkylineVectorValueModel)
33
- or is_many_skyline_parameter_like_configs(v)
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)
34
26
  for v in x
35
27
  )
@@ -1,19 +1,29 @@
1
- from phylogenie.skyline.matrix import SkylineMatrix, SkylineMatrixLike, skyline_matrix
1
+ from phylogenie.skyline.matrix import (
2
+ SkylineMatrix,
3
+ SkylineMatrixCoercible,
4
+ skyline_matrix,
5
+ )
2
6
  from phylogenie.skyline.parameter import (
3
7
  SkylineParameter,
4
8
  SkylineParameterLike,
5
9
  skyline_parameter,
6
10
  )
7
- from phylogenie.skyline.vector import SkylineVector, SkylineVectorLike, skyline_vector
11
+ from phylogenie.skyline.vector import (
12
+ SkylineVector,
13
+ SkylineVectorCoercible,
14
+ SkylineVectorLike,
15
+ skyline_vector,
16
+ )
8
17
 
9
18
  __all__ = [
10
19
  "skyline_matrix",
11
20
  "skyline_parameter",
12
21
  "skyline_vector",
22
+ "SkylineMatrix",
23
+ "SkylineMatrixCoercible",
13
24
  "SkylineParameter",
14
25
  "SkylineParameterLike",
15
26
  "SkylineVector",
27
+ "SkylineVectorCoercible",
16
28
  "SkylineVectorLike",
17
- "SkylineMatrix",
18
- "SkylineMatrixLike",
19
29
  ]
@@ -1,12 +1,13 @@
1
1
  from collections.abc import Callable, Iterator
2
- from typing import TypeGuard, Union, overload
2
+ from typing import TypeGuard, Union
3
3
 
4
4
  import phylogenie.typeguards as tg
5
5
  import phylogenie.typings as pgt
6
6
  from phylogenie.skyline.parameter import (
7
+ SkylineParameter,
7
8
  SkylineParameterLike,
8
- is_many_skyline_parameters_like,
9
9
  is_skyline_parameter_like,
10
+ skyline_parameter,
10
11
  )
11
12
  from phylogenie.skyline.vector import (
12
13
  SkylineVector,
@@ -18,51 +19,46 @@ from phylogenie.skyline.vector import (
18
19
  skyline_vector,
19
20
  )
20
21
 
21
- SkylineVector2D = pgt.Many[pgt.Many[SkylineParameterLike] | SkylineVector]
22
- SkylineMatrixParams = SkylineParameterLike | SkylineVector | SkylineVector2D
23
22
  SkylineMatrixOperand = Union[SkylineVectorOperand, "SkylineMatrix"]
24
- SkylineMatrixLike = Union[pgt.OneOrMany[SkylineVectorLike], "SkylineMatrix"]
23
+ SkylineMatrixCoercible = Union[
24
+ SkylineParameterLike, pgt.Many[SkylineVectorLike], "SkylineMatrix"
25
+ ]
25
26
 
26
27
 
27
- def is_skyline_vector2D(value: object) -> TypeGuard[SkylineVector2D]:
28
- return tg.is_many(value) and all(
29
- isinstance(v, SkylineVector) or is_many_skyline_parameters_like(v)
30
- for v in value
31
- )
28
+ def is_skyline_matrix_operand(x: object) -> TypeGuard[SkylineMatrixOperand]:
29
+ return isinstance(x, SkylineMatrix) or is_skyline_vector_operand(x)
32
30
 
33
31
 
34
32
  class SkylineMatrix:
35
33
  def __init__(
36
34
  self,
37
- params: SkylineMatrixParams | None = None,
38
- value: pgt.Many[pgt.OneOrMany2DScalars] | None = None,
39
- change_times: pgt.OneOrManyScalars | None = None,
35
+ params: pgt.Many[SkylineVectorLike] | None = None,
36
+ value: pgt.Many3DScalars | None = None,
37
+ change_times: pgt.ManyScalars | None = None,
40
38
  ):
41
39
  if params is not None and value is None and change_times is None:
42
- if is_skyline_parameter_like(params) or isinstance(params, SkylineVector):
43
- self.params = [skyline_vector(params, 1)]
44
- elif is_skyline_vector2D(params):
45
- self.params = [skyline_vector(param, N=len(params)) for param in params]
40
+ if is_many_skyline_vectors_like(params):
41
+ self.params = [skyline_vector(p, len(params)) for p in params]
46
42
  else:
47
43
  raise TypeError(
48
- f"It is impossible to create a SkylineMatrix from `params` {params} of type {type(params)}.Please provide either:\n"
49
- "- a SkylineParameterLike object (i.e., a SkylineParameter or a scalar),\n"
50
- "- a SkylineVector,\n"
51
- "- or a SkylineVector2D: a sequence containing SkylineVectors and/or sequences of SkylineParameterLike objects."
44
+ 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)."
52
45
  )
53
46
  elif value is not None and change_times is not None:
54
- Ns = {len(matrix) for matrix in value if tg.is_many(matrix)}
55
- if len(Ns) > 1:
56
- raise ValueError(
57
- f"All matrices in the `value` must be scalars or have the same length to create a SkylineMatrix (got value={value} with row lengths={Ns})."
47
+ if tg.is_many_3D_scalars(value):
48
+ matrix_lengths = {len(matrix) for matrix in value}
49
+ if any(ml != len(value[0]) for ml in matrix_lengths):
50
+ raise ValueError(
51
+ f"All matrices in the `value` of a SkylineMatrix must have the same length (got value={value} with matrix lengths={matrix_lengths})."
52
+ )
53
+ else:
54
+ raise TypeError(
55
+ f"It is impossible to create a SkylineMatrix from `value` {value} of type {type(value)}. Please provide a nested (3D) sequence of scalar values."
58
56
  )
59
- N = Ns.pop() if Ns else 1
60
- value = [[[x] * N] * N if isinstance(x, pgt.Scalar) else x for x in value]
61
57
  self.params = [
62
58
  SkylineVector(
63
59
  value=[matrix[i] for matrix in value], change_times=change_times
64
60
  )
65
- for i in range(N)
61
+ for i in range(len(value[0]))
66
62
  ]
67
63
  else:
68
64
  raise ValueError(
@@ -75,23 +71,27 @@ class SkylineMatrix:
75
71
 
76
72
  @property
77
73
  def change_times(self) -> pgt.Vector1D:
78
- return tuple(sorted(set([t for row in self.params for t in row.change_times])))
74
+ return sorted(set([t for row in self.params for t in row.change_times]))
79
75
 
80
76
  @property
81
77
  def value(self) -> pgt.Vector3D:
82
- return tuple(self.get_value_at_time(t) for t in (0, *self.change_times))
78
+ return [self.get_value_at_time(t) for t in (0, *self.change_times)]
83
79
 
84
80
  def get_value_at_time(self, time: pgt.Scalar) -> pgt.Vector2D:
85
- return tuple(param.get_value_at_time(time) for param in self.params)
81
+ return [param.get_value_at_time(time) for param in self.params]
86
82
 
87
83
  def operate(
88
84
  self,
89
85
  other: SkylineMatrixOperand,
90
- func: Callable[[SkylineVector, SkylineVector], SkylineVector],
86
+ func: Callable[
87
+ [SkylineVector, SkylineVector | SkylineParameter], SkylineVector
88
+ ],
91
89
  ) -> "SkylineMatrix":
92
90
  if not is_skyline_matrix_operand(other):
93
91
  return NotImplemented
94
- other = skyline_matrix(other, self.N)
92
+ if is_skyline_vector_operand(other):
93
+ other = skyline_vector(other, N=self.N)
94
+ assert isinstance(other, SkylineVector | SkylineMatrix)
95
95
  return SkylineMatrix(
96
96
  [func(p1, p2) for p1, p2 in zip(self.params, other.params)]
97
97
  )
@@ -139,40 +139,34 @@ class SkylineMatrix:
139
139
  def __len__(self) -> int:
140
140
  return self.N
141
141
 
142
- @overload
143
- def __getitem__(self, item: int) -> SkylineVector: ...
144
- @overload
145
- def __getitem__(self, item: slice) -> "SkylineMatrix": ...
146
- def __getitem__(self, item: int | slice) -> "SkylineVector | SkylineMatrix":
147
- if isinstance(item, slice):
148
- return SkylineMatrix(self.params[item])
142
+ def __getitem__(self, item: int) -> "SkylineVector":
149
143
  return self.params[item]
150
144
 
151
145
  def __setitem__(self, item: int, value: SkylineVectorLike) -> None:
152
146
  if not is_skyline_vector_like(value):
153
- raise TypeError(f"Expected a SkylineVectorLike, got {type(value)}.")
147
+ raise TypeError(
148
+ f"It is impossible to set item {item} of SkylineMatrix with value {value} of type {type(value)}. Please provide a SkylineVectorLike object (i.e., a SkylineVector or a sequence of scalars and/or SkylineParameters)."
149
+ )
154
150
  self.params[item] = skyline_vector(value, N=self.N)
155
151
 
156
152
 
157
153
  def skyline_matrix(
158
- x: SkylineMatrixLike, N: int, zero_diagonal: bool = False
154
+ x: SkylineMatrixCoercible, N: int, zero_diagonal: bool = False
159
155
  ) -> SkylineMatrix:
160
- if is_skyline_vector_like(x):
161
- x = SkylineMatrix([skyline_vector(x, N)] * N)
156
+ if is_skyline_parameter_like(x):
157
+ x = SkylineMatrix([[skyline_parameter(x)] * N] * N)
162
158
  if zero_diagonal:
163
159
  for i in range(N):
164
160
  x[i][i] = 0
165
- return x.T
166
-
167
- if is_many_skyline_vectors_like(x):
168
- x = SkylineMatrix([skyline_vector(v, N) for v in x])
169
-
161
+ return x
162
+ elif is_many_skyline_vectors_like(x):
163
+ x = SkylineMatrix(x)
170
164
  if not isinstance(x, SkylineMatrix):
171
165
  raise TypeError(
172
166
  f"It is impossible to coerce {x} of type {type(x)} into a SkylineMatrix. Please provide either:\n"
173
167
  "- a SkylineMatrix,\n"
174
- "- a SkylineVectorLike: a SkylineParameterLike (i.e., a SkylineParameter or a scalar) or a sequence of them,\n"
175
- "- or a sequence of SkylineVectorLike objects."
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)."
176
170
  )
177
171
 
178
172
  if x.N != N:
@@ -184,7 +178,3 @@ def skyline_matrix(
184
178
  raise ValueError(f"Expected a SkylineMatrix with zero diagonal, but got {x}.")
185
179
 
186
180
  return x
187
-
188
-
189
- def is_skyline_matrix_operand(x: object) -> TypeGuard[SkylineMatrixOperand]:
190
- return isinstance(x, SkylineMatrix) or is_skyline_vector_operand(x)
@@ -4,32 +4,51 @@ from typing import TypeGuard, Union
4
4
 
5
5
  import phylogenie.typeguards as tg
6
6
  import phylogenie.typings as pgt
7
- from phylogenie.utils import vectorify1D
8
7
 
9
8
  SkylineParameterLike = Union[pgt.Scalar, "SkylineParameter"]
10
9
 
11
10
 
11
+ def is_skyline_parameter_like(x: object) -> TypeGuard[SkylineParameterLike]:
12
+ return isinstance(x, pgt.Scalar | SkylineParameter)
13
+
14
+
15
+ def is_many_skyline_parameters_like(
16
+ x: object,
17
+ ) -> TypeGuard[pgt.Many[SkylineParameterLike]]:
18
+ return tg.is_many(x) and all(is_skyline_parameter_like(v) for v in x)
19
+
20
+
12
21
  class SkylineParameter:
13
22
  def __init__(
14
23
  self,
15
24
  value: pgt.OneOrManyScalars,
16
- change_times: pgt.OneOrManyScalars | None = None,
25
+ change_times: pgt.ManyScalars | None = None,
17
26
  ) -> None:
18
- value = vectorify1D(value)
19
- change_times = vectorify1D(change_times)
27
+ if isinstance(value, pgt.Scalar):
28
+ value = [value]
29
+ elif not tg.is_many_scalars(value):
30
+ raise TypeError(
31
+ f"It is impossible to create a SkylineParameter from `value` {value} of type {type(value)}. Please provide a scalar or a sequence of scalars."
32
+ )
33
+
34
+ if change_times is None:
35
+ change_times = []
36
+ elif not tg.is_many_scalars(change_times):
37
+ raise TypeError(
38
+ f"It is impossible to create a SkylineParameter from `change_times` {change_times} of type {type(change_times)}. Please provide a sequence of scalars."
39
+ )
40
+
20
41
  if len(value) != len(change_times) + 1:
21
42
  raise ValueError(
22
43
  f"`value` must have exactly one more element than `change_times` (got value={value} of length {len(value)} and change_times={change_times} of length {len(change_times)})."
23
44
  )
24
45
 
25
- value_ = [value[0]]
26
- change_times_: list[pgt.Scalar] = []
46
+ self.value = [value[0]]
47
+ self.change_times: list[pgt.Scalar] = []
27
48
  for i in range(1, len(value)):
28
49
  if value[i] != value[i - 1]:
29
- value_.append(value[i])
30
- change_times_.append(change_times[i - 1])
31
- self.value = tuple(value_)
32
- self.change_times = tuple(change_times_)
50
+ self.value.append(value[i])
51
+ self.value.append(change_times[i - 1])
33
52
 
34
53
  def get_value_at_time(self, t: pgt.Scalar) -> pgt.Scalar:
35
54
  return self.value[bisect_right(self.change_times, t)]
@@ -85,13 +104,3 @@ class SkylineParameter:
85
104
 
86
105
  def skyline_parameter(x: SkylineParameterLike) -> SkylineParameter:
87
106
  return SkylineParameter(x) if isinstance(x, pgt.Scalar) else x
88
-
89
-
90
- def is_skyline_parameter_like(x: object) -> TypeGuard[SkylineParameterLike]:
91
- return isinstance(x, pgt.Scalar | SkylineParameter)
92
-
93
-
94
- def is_many_skyline_parameters_like(
95
- x: object,
96
- ) -> TypeGuard[pgt.Many[SkylineParameterLike]]:
97
- return tg.is_many(x) and all(is_skyline_parameter_like(v) for v in x)