phylogenie 2.1.25__tar.gz → 2.1.26__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 (38) hide show
  1. {phylogenie-2.1.25 → phylogenie-2.1.26}/PKG-INFO +1 -1
  2. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/__init__.py +2 -2
  3. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/generators/configs.py +8 -4
  4. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/generators/factories.py +12 -5
  5. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/generators/trees.py +4 -9
  6. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/treesimulator/__init__.py +4 -4
  7. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/treesimulator/events/__init__.py +5 -1
  8. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/treesimulator/events/base.py +19 -1
  9. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/treesimulator/events/contact_tracing.py +29 -14
  10. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/treesimulator/events/core.py +13 -5
  11. {phylogenie-2.1.25/phylogenie/treesimulator → phylogenie-2.1.26/phylogenie/treesimulator/events}/mutations.py +25 -39
  12. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/treesimulator/features.py +1 -1
  13. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/treesimulator/gillespie.py +6 -14
  14. {phylogenie-2.1.25 → phylogenie-2.1.26}/pyproject.toml +1 -1
  15. {phylogenie-2.1.25 → phylogenie-2.1.26}/LICENSE.txt +0 -0
  16. {phylogenie-2.1.25 → phylogenie-2.1.26}/README.md +0 -0
  17. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/draw.py +0 -0
  18. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/generators/__init__.py +0 -0
  19. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/generators/alisim.py +0 -0
  20. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/generators/dataset.py +0 -0
  21. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/generators/typeguards.py +0 -0
  22. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/io/__init__.py +0 -0
  23. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/io/fasta.py +0 -0
  24. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/io/newick.py +0 -0
  25. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/io/nexus.py +0 -0
  26. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/main.py +0 -0
  27. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/models.py +0 -0
  28. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/msa.py +0 -0
  29. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/py.typed +0 -0
  30. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/skyline/__init__.py +0 -0
  31. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/skyline/matrix.py +0 -0
  32. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/skyline/parameter.py +0 -0
  33. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/skyline/vector.py +0 -0
  34. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/tree.py +0 -0
  35. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/treesimulator/model.py +0 -0
  36. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/typeguards.py +0 -0
  37. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/typings.py +0 -0
  38. {phylogenie-2.1.25 → phylogenie-2.1.26}/phylogenie/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: phylogenie
3
- Version: 2.1.25
3
+ Version: 2.1.26
4
4
  Summary: Generate phylogenetic datasets with minimal setup effort
5
5
  Author: Gabriele Marino
6
6
  Author-email: gabmarino.8601@gmail.com
@@ -31,9 +31,9 @@ from phylogenie.treesimulator import (
31
31
  BirthWithContactTracing,
32
32
  Death,
33
33
  Event,
34
+ EventType,
34
35
  Migration,
35
36
  Mutation,
36
- MutationTargetType,
37
37
  Sampling,
38
38
  SamplingWithContactTracing,
39
39
  generate_trees,
@@ -85,9 +85,9 @@ __all__ = [
85
85
  "BirthWithContactTracing",
86
86
  "Death",
87
87
  "Event",
88
+ "EventType",
88
89
  "Migration",
89
90
  "Mutation",
90
- "MutationTargetType",
91
91
  "Sampling",
92
92
  "SamplingWithContactTracing",
93
93
  "get_BD_events",
@@ -1,6 +1,6 @@
1
1
  import phylogenie.typings as pgt
2
2
  from phylogenie.models import Distribution, StrictBaseModel
3
- from phylogenie.treesimulator import MutationTargetType
3
+ from phylogenie.treesimulator import EventType
4
4
 
5
5
  Integer = str | int
6
6
  Scalar = str | pgt.Scalar
@@ -29,6 +29,10 @@ SkylineVector = str | pgt.Scalar | pgt.Many[SkylineParameter] | SkylineVectorMod
29
29
  SkylineMatrix = str | pgt.Scalar | pgt.Many[SkylineVector] | SkylineMatrixModel | None
30
30
 
31
31
 
32
- class Mutation(StrictBaseModel):
33
- rate: Scalar
34
- rate_scalers: dict[MutationTargetType, Distribution]
32
+ class Event(StrictBaseModel):
33
+ state: str | None = None
34
+ rate: SkylineParameter
35
+
36
+
37
+ class Mutation(Event):
38
+ rate_scalers: dict[EventType, Distribution]
@@ -222,11 +222,18 @@ def distribution(x: Distribution, data: dict[str, Any]) -> Distribution:
222
222
  return Distribution(type=x.type, **args)
223
223
 
224
224
 
225
- def mutation(x: cfg.Mutation, data: dict[str, Any]) -> Mutation:
226
- return Mutation(
227
- scalar(x.rate, data),
228
- {k: distribution(v, data) for k, v in x.rate_scalers.items()},
229
- )
225
+ def mutations(
226
+ x: list[cfg.Mutation], data: dict[str, Any], states: set[str]
227
+ ) -> list[Mutation]:
228
+ mutations: list[Mutation] = []
229
+ for m in x:
230
+ rate = skyline_parameter(m.rate, data)
231
+ rate_scalers = {k: distribution(v, data) for k, v in m.rate_scalers.items()}
232
+ if m.state is None:
233
+ mutations.extend(Mutation(s, rate, rate_scalers) for s in states)
234
+ else:
235
+ mutations.append(Mutation(m.state, rate, rate_scalers))
236
+ return mutations
230
237
 
231
238
 
232
239
  def data(context: dict[str, Distribution] | None, rng: Generator) -> dict[str, Any]:
@@ -12,7 +12,7 @@ from phylogenie.generators.dataset import DatasetGenerator, DataType
12
12
  from phylogenie.generators.factories import (
13
13
  data,
14
14
  integer,
15
- mutation,
15
+ mutations,
16
16
  scalar,
17
17
  skyline_matrix,
18
18
  skyline_parameter,
@@ -47,7 +47,7 @@ class ParameterizationType(str, Enum):
47
47
 
48
48
  class TreeDatasetGenerator(DatasetGenerator):
49
49
  data_type: Literal[DataType.TREES] = DataType.TREES
50
- mutations: list[cfg.Mutation] | None = None
50
+ mutations: list[cfg.Mutation] = Field(default_factory=lambda: [])
51
51
  min_tips: cfg.Integer = 1
52
52
  max_tips: cfg.Integer | None = None
53
53
  max_time: cfg.Scalar = np.inf
@@ -67,14 +67,9 @@ class TreeDatasetGenerator(DatasetGenerator):
67
67
  if self.init_state is None
68
68
  else self.init_state.format(**data)
69
69
  )
70
- mutations = (
71
- None
72
- if self.mutations is None
73
- else [mutation(m, data) for m in self.mutations]
74
- )
70
+ states = {e.state for e in self._get_events(data)}
75
71
  return simulate_tree(
76
- events=self._get_events(data),
77
- mutations=mutations,
72
+ events=self._get_events(data) + mutations(self.mutations, data, states),
78
73
  min_tips=integer(self.min_tips, data),
79
74
  max_tips=None if self.max_tips is None else integer(self.max_tips, data),
80
75
  max_time=scalar(self.max_time, data),
@@ -3,7 +3,9 @@ from phylogenie.treesimulator.events import (
3
3
  BirthWithContactTracing,
4
4
  Death,
5
5
  Event,
6
+ EventType,
6
7
  Migration,
8
+ Mutation,
7
9
  Sampling,
8
10
  SamplingWithContactTracing,
9
11
  get_BD_events,
@@ -13,22 +15,20 @@ from phylogenie.treesimulator.events import (
13
15
  get_contact_tracing_events,
14
16
  get_epidemiological_events,
15
17
  get_FBD_events,
18
+ get_mutation_id,
16
19
  )
17
20
  from phylogenie.treesimulator.features import Feature, set_features
18
21
  from phylogenie.treesimulator.gillespie import generate_trees, simulate_tree
19
22
  from phylogenie.treesimulator.model import get_node_state
20
- from phylogenie.treesimulator.mutations import Mutation
21
- from phylogenie.treesimulator.mutations import TargetType as MutationTargetType
22
- from phylogenie.treesimulator.mutations import get_mutation_id
23
23
 
24
24
  __all__ = [
25
25
  "Birth",
26
26
  "BirthWithContactTracing",
27
27
  "Death",
28
28
  "Event",
29
+ "EventType",
29
30
  "Migration",
30
31
  "Mutation",
31
- "MutationTargetType",
32
32
  "Sampling",
33
33
  "SamplingWithContactTracing",
34
34
  "get_BD_events",
@@ -1,4 +1,4 @@
1
- from phylogenie.treesimulator.events.base import Event
1
+ from phylogenie.treesimulator.events.base import Event, EventType
2
2
  from phylogenie.treesimulator.events.contact_tracing import (
3
3
  BirthWithContactTracing,
4
4
  SamplingWithContactTracing,
@@ -16,12 +16,14 @@ from phylogenie.treesimulator.events.core import (
16
16
  get_epidemiological_events,
17
17
  get_FBD_events,
18
18
  )
19
+ from phylogenie.treesimulator.events.mutations import Mutation, get_mutation_id
19
20
 
20
21
  __all__ = [
21
22
  "Birth",
22
23
  "BirthWithContactTracing",
23
24
  "Death",
24
25
  "Event",
26
+ "EventType",
25
27
  "Migration",
26
28
  "Sampling",
27
29
  "SamplingWithContactTracing",
@@ -32,4 +34,6 @@ __all__ = [
32
34
  "get_contact_tracing_events",
33
35
  "get_epidemiological_events",
34
36
  "get_FBD_events",
37
+ "Mutation",
38
+ "get_mutation_id",
35
39
  ]
@@ -1,4 +1,6 @@
1
1
  from abc import ABC, abstractmethod
2
+ from enum import Enum
3
+ from typing import Any
2
4
 
3
5
  import numpy as np
4
6
  from numpy.random import Generator
@@ -7,10 +9,24 @@ from phylogenie.skyline import SkylineParameterLike, skyline_parameter
7
9
  from phylogenie.treesimulator.model import Model
8
10
 
9
11
 
12
+ class EventType(str, Enum):
13
+ BIRTH = "birth"
14
+ DEATH = "death"
15
+ MIGRATION = "migration"
16
+ SAMPLING = "sampling"
17
+ MUTATION = "mutation"
18
+ BIRTH_WITH_CT = "birth_with_contact_tracing"
19
+ SAMPLING_WITH_CT = "sampling_with_contact_tracing"
20
+
21
+
10
22
  class Event(ABC):
23
+ type: EventType
24
+
11
25
  def __init__(self, state: str, rate: SkylineParameterLike):
12
26
  self.state = state
13
27
  self.rate = skyline_parameter(rate)
28
+ if any(v < 0 for v in self.rate.value):
29
+ raise ValueError("Event rates must be non-negative.")
14
30
 
15
31
  def draw_individual(self, model: Model, rng: Generator) -> int:
16
32
  return rng.choice(model.get_population(self.state))
@@ -23,4 +39,6 @@ class Event(ABC):
23
39
  return rate * n_individuals
24
40
 
25
41
  @abstractmethod
26
- def apply(self, model: Model, time: float, rng: Generator) -> None: ...
42
+ def apply(
43
+ self, model: Model, events: "list[Event]", time: float, rng: Generator
44
+ ) -> dict[str, Any] | None: ...
@@ -1,11 +1,12 @@
1
1
  from collections import defaultdict
2
2
  from collections.abc import Sequence
3
+ from copy import deepcopy
3
4
 
4
5
  import numpy as np
5
6
  from numpy.random import Generator
6
7
 
7
8
  from phylogenie.skyline import SkylineParameterLike, skyline_parameter
8
- from phylogenie.treesimulator.events.base import Event
9
+ from phylogenie.treesimulator.events.base import Event, EventType
9
10
  from phylogenie.treesimulator.events.core import Birth, Death, Migration, Sampling
10
11
  from phylogenie.treesimulator.model import Model
11
12
 
@@ -13,20 +14,22 @@ CT_POSTFIX = "-CT"
13
14
  CONTACTS_KEY = "CONTACTS"
14
15
 
15
16
 
16
- def _get_CT_state(state: str) -> str:
17
+ def get_CT_state(state: str) -> str:
17
18
  return f"{state}{CT_POSTFIX}"
18
19
 
19
20
 
20
- def _is_CT_state(state: str) -> bool:
21
+ def is_CT_state(state: str) -> bool:
21
22
  return state.endswith(CT_POSTFIX)
22
23
 
23
24
 
24
25
  class BirthWithContactTracing(Event):
26
+ type = EventType.BIRTH_WITH_CT
27
+
25
28
  def __init__(self, state: str, rate: SkylineParameterLike, child_state: str):
26
29
  super().__init__(state, rate)
27
30
  self.child_state = child_state
28
31
 
29
- def apply(self, model: Model, time: float, rng: Generator) -> None:
32
+ def apply(self, model: Model, events: list[Event], time: float, rng: Generator):
30
33
  individual = self.draw_individual(model, rng)
31
34
  new_individual = model.birth_from(individual, self.child_state, time)
32
35
  if CONTACTS_KEY not in model.context:
@@ -39,6 +42,8 @@ class BirthWithContactTracing(Event):
39
42
 
40
43
 
41
44
  class SamplingWithContactTracing(Event):
45
+ type = EventType.SAMPLING_WITH_CT
46
+
42
47
  def __init__(
43
48
  self,
44
49
  state: str,
@@ -50,7 +55,7 @@ class SamplingWithContactTracing(Event):
50
55
  self.max_notified_contacts = max_notified_contacts
51
56
  self.notification_probability = skyline_parameter(notification_probability)
52
57
 
53
- def apply(self, model: Model, time: float, rng: Generator) -> None:
58
+ def apply(self, model: Model, events: list[Event], time: float, rng: Generator):
54
59
  individual = self.draw_individual(model, rng)
55
60
  model.sample(individual, time, True)
56
61
  population = model.get_population()
@@ -61,8 +66,8 @@ class SamplingWithContactTracing(Event):
61
66
  if contact in population:
62
67
  state = model.get_state(contact)
63
68
  p = self.notification_probability.get_value_at_time(time)
64
- if not _is_CT_state(state) and rng.random() < p:
65
- model.migrate(contact, _get_CT_state(state), time)
69
+ if not is_CT_state(state) and rng.random() < p:
70
+ model.migrate(contact, get_CT_state(state), time)
66
71
 
67
72
  def __repr__(self) -> str:
68
73
  return f"SamplingWithContactTracing(state={self.state}, rate={self.rate}, max_notified_contacts={self.max_notified_contacts}, notification_probability={self.notification_probability})"
@@ -80,17 +85,24 @@ def get_contact_tracing_events(
80
85
  sampling_rate_after_notification = skyline_parameter(
81
86
  sampling_rate_after_notification
82
87
  )
83
- for event in events:
84
- state, rate = event.state, event.rate
88
+ for event in [deepcopy(e) for e in events]:
85
89
  if isinstance(event, Migration):
86
90
  ct_events.append(event)
87
91
  ct_events.append(
88
- Migration(_get_CT_state(state), rate, _get_CT_state(event.target_state))
92
+ Migration(
93
+ get_CT_state(event.state),
94
+ event.rate,
95
+ get_CT_state(event.target_state),
96
+ )
89
97
  )
90
98
  elif isinstance(event, Birth):
91
- ct_events.append(BirthWithContactTracing(state, rate, event.child_state))
92
99
  ct_events.append(
93
- BirthWithContactTracing(_get_CT_state(state), rate, event.child_state)
100
+ BirthWithContactTracing(event.state, event.rate, event.child_state)
101
+ )
102
+ ct_events.append(
103
+ BirthWithContactTracing(
104
+ get_CT_state(event.state), event.rate, event.child_state
105
+ )
94
106
  )
95
107
  elif isinstance(event, Sampling):
96
108
  if not event.removal:
@@ -99,7 +111,10 @@ def get_contact_tracing_events(
99
111
  )
100
112
  ct_events.append(
101
113
  SamplingWithContactTracing(
102
- state, rate, max_notified_contacts, notification_probability
114
+ event.state,
115
+ event.rate,
116
+ max_notified_contacts,
117
+ notification_probability,
103
118
  )
104
119
  )
105
120
  elif isinstance(event, Death):
@@ -116,7 +131,7 @@ def get_contact_tracing_events(
116
131
  ):
117
132
  ct_events.append(
118
133
  SamplingWithContactTracing(
119
- _get_CT_state(state),
134
+ get_CT_state(state),
120
135
  sampling_rate_after_notification,
121
136
  max_notified_contacts,
122
137
  notification_probability,
@@ -7,7 +7,7 @@ from phylogenie.skyline import (
7
7
  skyline_matrix,
8
8
  skyline_vector,
9
9
  )
10
- from phylogenie.treesimulator.events.base import Event
10
+ from phylogenie.treesimulator.events.base import Event, EventType
11
11
  from phylogenie.treesimulator.model import Model
12
12
 
13
13
  INFECTIOUS_STATE = "I"
@@ -16,11 +16,13 @@ SUPERSPREADER_STATE = "S"
16
16
 
17
17
 
18
18
  class Birth(Event):
19
+ type = EventType.BIRTH
20
+
19
21
  def __init__(self, state: str, rate: SkylineParameterLike, child_state: str):
20
22
  super().__init__(state, rate)
21
23
  self.child_state = child_state
22
24
 
23
- def apply(self, model: Model, time: float, rng: Generator) -> None:
25
+ def apply(self, model: Model, events: list[Event], time: float, rng: Generator):
24
26
  individual = self.draw_individual(model, rng)
25
27
  model.birth_from(individual, self.child_state, time)
26
28
 
@@ -29,7 +31,9 @@ class Birth(Event):
29
31
 
30
32
 
31
33
  class Death(Event):
32
- def apply(self, model: Model, time: float, rng: Generator) -> None:
34
+ type = EventType.DEATH
35
+
36
+ def apply(self, model: Model, events: list[Event], time: float, rng: Generator):
33
37
  individual = self.draw_individual(model, rng)
34
38
  model.remove(individual, time)
35
39
 
@@ -38,11 +42,13 @@ class Death(Event):
38
42
 
39
43
 
40
44
  class Migration(Event):
45
+ type = EventType.MIGRATION
46
+
41
47
  def __init__(self, state: str, rate: SkylineParameterLike, target_state: str):
42
48
  super().__init__(state, rate)
43
49
  self.target_state = target_state
44
50
 
45
- def apply(self, model: Model, time: float, rng: Generator) -> None:
51
+ def apply(self, model: Model, events: list[Event], time: float, rng: Generator):
46
52
  individual = self.draw_individual(model, rng)
47
53
  model.migrate(individual, self.target_state, time)
48
54
 
@@ -51,11 +57,13 @@ class Migration(Event):
51
57
 
52
58
 
53
59
  class Sampling(Event):
60
+ type = EventType.SAMPLING
61
+
54
62
  def __init__(self, state: str, rate: SkylineParameterLike, removal: bool):
55
63
  super().__init__(state, rate)
56
64
  self.removal = removal
57
65
 
58
- def apply(self, model: Model, time: float, rng: Generator) -> None:
66
+ def apply(self, model: Model, events: list[Event], time: float, rng: Generator):
59
67
  individual = self.draw_individual(model, rng)
60
68
  model.sample(individual, time, self.removal)
61
69
 
@@ -1,22 +1,17 @@
1
1
  import re
2
2
  from copy import deepcopy
3
- from enum import Enum
4
- from typing import Any, Type
3
+ from typing import Any
5
4
 
6
5
  from numpy.random import Generator
7
6
 
8
7
  from phylogenie.models import Distribution
8
+ from phylogenie.skyline import SkylineParameterLike
9
+ from phylogenie.treesimulator.events.base import Event, EventType
9
10
  from phylogenie.treesimulator.events.contact_tracing import (
10
11
  BirthWithContactTracing,
11
12
  SamplingWithContactTracing,
12
13
  )
13
- from phylogenie.treesimulator.events.core import (
14
- Birth,
15
- Death,
16
- Event,
17
- Migration,
18
- Sampling,
19
- )
14
+ from phylogenie.treesimulator.events.core import Birth, Death, Migration, Sampling
20
15
  from phylogenie.treesimulator.model import Model
21
16
 
22
17
  MUTATION_PREFIX = "MUT-"
@@ -40,26 +35,16 @@ def get_mutation_id(node_name: str) -> int:
40
35
  return 0
41
36
 
42
37
 
43
- class TargetType(str, Enum):
44
- BIRTH = "birth"
45
- DEATH = "death"
46
- MIGRATION = "migration"
47
- SAMPLING = "sampling"
48
-
49
-
50
- EVENT_TARGET_TYPES: dict[Type[Event], TargetType] = {
51
- Birth: TargetType.BIRTH,
52
- BirthWithContactTracing: TargetType.BIRTH,
53
- Death: TargetType.DEATH,
54
- Migration: TargetType.MIGRATION,
55
- Sampling: TargetType.SAMPLING,
56
- SamplingWithContactTracing: TargetType.SAMPLING,
57
- }
38
+ class Mutation(Event):
39
+ type = EventType.MUTATION
58
40
 
59
-
60
- class Mutation:
61
- def __init__(self, rate: float, rate_scalers: dict[TargetType, Distribution]):
62
- self.rate = rate
41
+ def __init__(
42
+ self,
43
+ state: str,
44
+ rate: SkylineParameterLike,
45
+ rate_scalers: dict[EventType, Distribution],
46
+ ):
47
+ super().__init__(state, rate)
63
48
  self.rate_scalers = rate_scalers
64
49
 
65
50
  def apply(
@@ -70,11 +55,10 @@ class Mutation:
70
55
  model.context[NEXT_MUTATION_ID] += 1
71
56
  mutation_id = model.context[NEXT_MUTATION_ID]
72
57
 
73
- individual = rng.choice(model.get_population())
74
- state = model.get_state(individual)
75
- model.migrate(individual, _get_mutated_state(mutation_id, state), time)
58
+ individual = self.draw_individual(model, rng)
59
+ model.migrate(individual, _get_mutated_state(mutation_id, self.state), time)
76
60
 
77
- rate_scalers: dict[TargetType, float] = {
61
+ rate_scalers: dict[EventType, float] = {
78
62
  target_type: getattr(rng, rate_scaler.type)(**rate_scaler.args)
79
63
  for target_type, rate_scaler in self.rate_scalers.items()
80
64
  }
@@ -83,22 +67,24 @@ class Mutation:
83
67
  for event in [
84
68
  deepcopy(e)
85
69
  for e in events
86
- if _get_mutation(state) == _get_mutation(e.state)
70
+ if _get_mutation(self.state) == _get_mutation(e.state)
87
71
  ]:
88
72
  event.state = _get_mutated_state(mutation_id, event.state)
73
+
89
74
  if isinstance(event, Birth | BirthWithContactTracing):
90
75
  event.child_state = _get_mutated_state(mutation_id, event.child_state)
91
76
  elif isinstance(event, Migration):
92
77
  event.target_state = _get_mutated_state(mutation_id, event.target_state)
93
- elif not isinstance(event, Death | Sampling | SamplingWithContactTracing):
78
+ elif not isinstance(
79
+ event, Mutation | Death | Sampling | SamplingWithContactTracing
80
+ ):
94
81
  raise ValueError(
95
82
  f"Mutation not implemented for event of type {type(event)}."
96
83
  )
97
84
 
98
- target_type = EVENT_TARGET_TYPES[type(event)]
99
- if target_type in rate_scalers:
100
- event.rate *= rate_scalers[target_type]
101
- metadata[f"{MUTATION_PREFIX}{mutation_id}.{target_type}.rate.value"] = (
85
+ if event.type in rate_scalers:
86
+ event.rate *= rate_scalers[event.type]
87
+ metadata[f"{MUTATION_PREFIX}{mutation_id}.{event.type}.rate.value"] = (
102
88
  event.rate.value[0]
103
89
  if len(event.rate.value) == 1
104
90
  else list(event.rate.value)
@@ -109,4 +95,4 @@ class Mutation:
109
95
  return metadata
110
96
 
111
97
  def __repr__(self) -> str:
112
- return f"Mutation(rate={self.rate}, rate_scalers={self.rate_scalers})"
98
+ return f"Mutation(state={self.state}, rate={self.rate}, rate_scalers={self.rate_scalers})"
@@ -2,8 +2,8 @@ from collections.abc import Iterable
2
2
  from enum import Enum
3
3
 
4
4
  from phylogenie.tree import Tree
5
+ from phylogenie.treesimulator.events.mutations import get_mutation_id
5
6
  from phylogenie.treesimulator.model import get_node_state
6
- from phylogenie.treesimulator.mutations import get_mutation_id
7
7
  from phylogenie.utils import (
8
8
  get_node_depth_levels,
9
9
  get_node_depths,
@@ -14,12 +14,10 @@ from phylogenie.tree import Tree
14
14
  from phylogenie.treesimulator.events import Event
15
15
  from phylogenie.treesimulator.features import Feature, set_features
16
16
  from phylogenie.treesimulator.model import Model
17
- from phylogenie.treesimulator.mutations import Mutation
18
17
 
19
18
 
20
19
  def simulate_tree(
21
20
  events: Sequence[Event],
22
- mutations: Sequence[Mutation] | None = None,
23
21
  min_tips: int = 1,
24
22
  max_tips: int | None = None,
25
23
  max_time: float = np.inf,
@@ -43,9 +41,6 @@ def simulate_tree(
43
41
  elif init_state not in states:
44
42
  raise ValueError(f"Init state {init_state} not found in event states: {states}")
45
43
 
46
- if mutations is None:
47
- mutations = []
48
-
49
44
  rng = default_rng(seed)
50
45
  start_clock = time.perf_counter()
51
46
  while True:
@@ -72,7 +67,7 @@ def simulate_tree(
72
67
  instantaneous_events = [e for e, r in zip(run_events, rates) if r == np.inf]
73
68
  if instantaneous_events:
74
69
  event = instantaneous_events[rng.integers(len(instantaneous_events))]
75
- event.apply(model, current_time, rng)
70
+ event.apply(model, run_events, current_time, rng)
76
71
  continue
77
72
 
78
73
  if (
@@ -84,7 +79,6 @@ def simulate_tree(
84
79
  ):
85
80
  break
86
81
 
87
- rates.extend(m.rate for m in mutations)
88
82
  time_step = rng.exponential(1 / sum(rates))
89
83
  if current_time + time_step >= next_change_time:
90
84
  current_time = next_change_time
@@ -95,13 +89,11 @@ def simulate_tree(
95
89
  break
96
90
  current_time += time_step
97
91
 
98
- targets = run_events + list(mutations)
99
- target_idx = np.searchsorted(np.cumsum(rates) / sum(rates), rng.random())
100
- target = targets[int(target_idx)]
101
- if isinstance(target, Event):
102
- target.apply(model, current_time, rng)
103
- else:
104
- metadata.update(target.apply(model, run_events, current_time, rng))
92
+ event_idx = np.searchsorted(np.cumsum(rates) / sum(rates), rng.random())
93
+ event = run_events[int(event_idx)]
94
+ event_metadata = event.apply(model, run_events, current_time, rng)
95
+ if event_metadata is not None:
96
+ metadata.update(event_metadata)
105
97
 
106
98
  for individual in model.get_population():
107
99
  if rng.random() < sampling_probability_at_present:
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "phylogenie"
3
- version = "2.1.25"
3
+ version = "2.1.26"
4
4
  description = "Generate phylogenetic datasets with minimal setup effort"
5
5
  authors = ["Gabriele Marino <gabmarino.8601@gmail.com>"]
6
6
  readme = "README.md"
File without changes
File without changes