phylogenie 1.0.1__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.
@@ -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):
@@ -48,7 +48,7 @@ class ReMASTERGenerator(TreesGenerator):
48
48
  trajectory_attrs: dict[str, str | int | float] = Field(default_factory=dict)
49
49
 
50
50
  def _generate_one_from_extra_reactions(
51
- self, filename: str, rng: Generator, data: Data, reactions: list[Reaction]
51
+ self, filename: str, rng: Generator, data: pgt.Data, reactions: list[Reaction]
52
52
  ) -> None:
53
53
  generate_trees(
54
54
  tree_file_name=f"{filename}.nwk",
@@ -67,7 +67,7 @@ class ReMASTERGenerator(TreesGenerator):
67
67
  beast_path=self.beast_path,
68
68
  )
69
69
 
70
- def _generate_one(self, filename: str, rng: Generator, data: Data) -> None:
70
+ def _generate_one(self, filename: str, rng: Generator, data: pgt.Data) -> None:
71
71
  self._generate_one_from_extra_reactions(filename, rng, data, reactions=[])
72
72
 
73
73
 
@@ -75,25 +75,27 @@ class CanonicalReMASTERGenerator(ReMASTERGenerator):
75
75
  parameterization: Literal[ParameterizationType.CANONICAL] = (
76
76
  ParameterizationType.CANONICAL
77
77
  )
78
- birth_rates: cfg.SkylineVectorLikeConfig = 0
79
- death_rates: cfg.SkylineVectorLikeConfig = 0
80
- sampling_rates: cfg.SkylineVectorLikeConfig = 0
81
- removal_probabilities: cfg.SkylineVectorLikeConfig = 0
82
- migration_rates: cfg.SkylineMatrixLikeConfig = 0
83
- birth_rates_among_demes: cfg.SkylineMatrixLikeConfig = 0
84
-
85
- 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:
86
86
  reactions = get_canonical_reactions(
87
87
  populations=self.populations,
88
88
  sample_population=self.sample_population,
89
- birth_rates=skyline_vector_like_factory(self.birth_rates, data),
90
- death_rates=skyline_vector_like_factory(self.death_rates, data),
91
- sampling_rates=skyline_vector_like_factory(self.sampling_rates, data),
92
- 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(
93
93
  self.removal_probabilities, data
94
94
  ),
95
- migration_rates=skyline_matrix_like_factory(self.migration_rates, data),
96
- 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(
97
99
  self.birth_rates_among_demes, data
98
100
  ),
99
101
  )
@@ -104,31 +106,33 @@ class EpidemiologicalReMASTERGenerator(ReMASTERGenerator):
104
106
  parameterization: Literal[ParameterizationType.EPIDEMIOLOGICAL] = (
105
107
  ParameterizationType.EPIDEMIOLOGICAL
106
108
  )
107
- reproduction_numbers: cfg.SkylineVectorLikeConfig = 0
108
- become_uninfectious_rates: cfg.SkylineVectorLikeConfig = 0
109
- sampling_proportions: cfg.SkylineVectorLikeConfig = 0
110
- removal_probabilities: cfg.SkylineVectorLikeConfig = 0
111
- migration_rates: cfg.SkylineMatrixLikeConfig = 0
112
- reproduction_numbers_among_demes: cfg.SkylineMatrixLikeConfig = 0
113
-
114
- 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:
115
117
  reactions = get_epidemiological_reactions(
116
118
  populations=self.populations,
117
119
  sample_population=self.sample_population,
118
- reproduction_numbers=skyline_vector_like_factory(
120
+ reproduction_numbers=skyline_vector_coercible_factory(
119
121
  self.reproduction_numbers, data
120
122
  ),
121
- become_uninfectious_rates=skyline_vector_like_factory(
123
+ become_uninfectious_rates=skyline_vector_coercible_factory(
122
124
  self.become_uninfectious_rates, data
123
125
  ),
124
- sampling_proportions=skyline_vector_like_factory(
126
+ sampling_proportions=skyline_vector_coercible_factory(
125
127
  self.sampling_proportions, data
126
128
  ),
127
- removal_probabilities=skyline_vector_like_factory(
129
+ removal_probabilities=skyline_vector_coercible_factory(
128
130
  self.removal_probabilities, data
129
131
  ),
130
- migration_rates=skyline_matrix_like_factory(self.migration_rates, data),
131
- 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(
132
136
  self.reproduction_numbers_among_demes, data
133
137
  ),
134
138
  )
@@ -137,27 +141,31 @@ class EpidemiologicalReMASTERGenerator(ReMASTERGenerator):
137
141
 
138
142
  class FBDReMASTERGenerator(ReMASTERGenerator):
139
143
  parameterization: Literal[ParameterizationType.FBD] = ParameterizationType.FBD
140
- diversification: cfg.SkylineVectorLikeConfig = 0
141
- turnover: cfg.SkylineVectorLikeConfig = 0
142
- sampling_proportions: cfg.SkylineVectorLikeConfig = 0
143
- removal_probabilities: cfg.SkylineVectorLikeConfig = 0
144
- migration_rates: cfg.SkylineMatrixLikeConfig = 0
145
- diversification_between_types: cfg.SkylineMatrixLikeConfig = 0
146
-
147
- 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:
148
152
  reactions = get_FBD_reactions(
149
153
  populations=self.populations,
150
154
  sample_population=self.sample_population,
151
- diversification=skyline_vector_like_factory(self.diversification, data),
152
- turnover=skyline_vector_like_factory(self.turnover, data),
153
- 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(
154
160
  self.sampling_proportions, data
155
161
  ),
156
- removal_probabilities=skyline_vector_like_factory(
162
+ removal_probabilities=skyline_vector_coercible_factory(
157
163
  self.removal_probabilities, data
158
164
  ),
159
- migration_rates=skyline_matrix_like_factory(self.migration_rates, data),
160
- 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(
161
169
  self.diversification_between_types, data
162
170
  ),
163
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):
@@ -43,7 +43,7 @@ class TreeSimulatorGenerator(TreesGenerator):
43
43
  max_notified_contacts: cfg.IntConfig = 1
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)