phylogenie 2.1.25__py3-none-any.whl → 2.1.27__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.
- phylogenie/__init__.py +2 -2
- phylogenie/generators/configs.py +8 -4
- phylogenie/generators/factories.py +18 -6
- phylogenie/generators/trees.py +8 -9
- phylogenie/treesimulator/__init__.py +4 -4
- phylogenie/treesimulator/events/__init__.py +5 -1
- phylogenie/treesimulator/events/base.py +17 -1
- phylogenie/treesimulator/events/contact_tracing.py +29 -14
- phylogenie/treesimulator/events/core.py +13 -5
- phylogenie/treesimulator/{mutations.py → events/mutations.py} +33 -39
- phylogenie/treesimulator/features.py +1 -1
- phylogenie/treesimulator/gillespie.py +6 -14
- {phylogenie-2.1.25.dist-info → phylogenie-2.1.27.dist-info}/METADATA +1 -1
- {phylogenie-2.1.25.dist-info → phylogenie-2.1.27.dist-info}/RECORD +17 -17
- {phylogenie-2.1.25.dist-info → phylogenie-2.1.27.dist-info}/LICENSE.txt +0 -0
- {phylogenie-2.1.25.dist-info → phylogenie-2.1.27.dist-info}/WHEEL +0 -0
- {phylogenie-2.1.25.dist-info → phylogenie-2.1.27.dist-info}/entry_points.txt +0 -0
phylogenie/__init__.py
CHANGED
|
@@ -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",
|
phylogenie/generators/configs.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import phylogenie.typings as pgt
|
|
2
2
|
from phylogenie.models import Distribution, StrictBaseModel
|
|
3
|
-
from phylogenie.treesimulator import
|
|
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
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
class Event(StrictBaseModel):
|
|
33
|
+
state: str | None = None
|
|
34
|
+
rate: SkylineParameter
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Mutation(Event):
|
|
38
|
+
rate_scalers: dict[EventType, Distribution]
|
|
@@ -17,7 +17,7 @@ from phylogenie.skyline import (
|
|
|
17
17
|
SkylineVector,
|
|
18
18
|
SkylineVectorCoercible,
|
|
19
19
|
)
|
|
20
|
-
from phylogenie.treesimulator import Mutation
|
|
20
|
+
from phylogenie.treesimulator import EventType, Mutation
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def _eval_expression(expression: str, data: dict[str, Any]) -> Any:
|
|
@@ -222,11 +222,23 @@ def distribution(x: Distribution, data: dict[str, Any]) -> Distribution:
|
|
|
222
222
|
return Distribution(type=x.type, **args)
|
|
223
223
|
|
|
224
224
|
|
|
225
|
-
def
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
225
|
+
def mutations(
|
|
226
|
+
x: list[cfg.Mutation],
|
|
227
|
+
data: dict[str, Any],
|
|
228
|
+
states: set[str],
|
|
229
|
+
rates_to_log: list[EventType] | None,
|
|
230
|
+
) -> list[Mutation]:
|
|
231
|
+
mutations: list[Mutation] = []
|
|
232
|
+
for m in x:
|
|
233
|
+
rate = skyline_parameter(m.rate, data)
|
|
234
|
+
rate_scalers = {k: distribution(v, data) for k, v in m.rate_scalers.items()}
|
|
235
|
+
if m.state is None:
|
|
236
|
+
mutations.extend(
|
|
237
|
+
Mutation(s, rate, rate_scalers, rates_to_log) for s in states
|
|
238
|
+
)
|
|
239
|
+
else:
|
|
240
|
+
mutations.append(Mutation(m.state, rate, rate_scalers, rates_to_log))
|
|
241
|
+
return mutations
|
|
230
242
|
|
|
231
243
|
|
|
232
244
|
def data(context: dict[str, Distribution] | None, rng: Generator) -> dict[str, Any]:
|
phylogenie/generators/trees.py
CHANGED
|
@@ -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
|
-
|
|
15
|
+
mutations,
|
|
16
16
|
scalar,
|
|
17
17
|
skyline_matrix,
|
|
18
18
|
skyline_parameter,
|
|
@@ -23,6 +23,7 @@ from phylogenie.models import Distribution
|
|
|
23
23
|
from phylogenie.tree import Tree
|
|
24
24
|
from phylogenie.treesimulator import (
|
|
25
25
|
Event,
|
|
26
|
+
EventType,
|
|
26
27
|
Feature,
|
|
27
28
|
get_BD_events,
|
|
28
29
|
get_BDEI_events,
|
|
@@ -47,7 +48,8 @@ class ParameterizationType(str, Enum):
|
|
|
47
48
|
|
|
48
49
|
class TreeDatasetGenerator(DatasetGenerator):
|
|
49
50
|
data_type: Literal[DataType.TREES] = DataType.TREES
|
|
50
|
-
mutations: list[cfg.Mutation]
|
|
51
|
+
mutations: list[cfg.Mutation] = Field(default_factory=lambda: [])
|
|
52
|
+
rates_to_log: list[EventType] | None = None
|
|
51
53
|
min_tips: cfg.Integer = 1
|
|
52
54
|
max_tips: cfg.Integer | None = None
|
|
53
55
|
max_time: cfg.Scalar = np.inf
|
|
@@ -67,14 +69,11 @@ class TreeDatasetGenerator(DatasetGenerator):
|
|
|
67
69
|
if self.init_state is None
|
|
68
70
|
else self.init_state.format(**data)
|
|
69
71
|
)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
else [mutation(m, data) for m in self.mutations]
|
|
74
|
-
)
|
|
72
|
+
events = self._get_events(data)
|
|
73
|
+
states = {e.state for e in events}
|
|
74
|
+
events += mutations(self.mutations, data, states, self.rates_to_log)
|
|
75
75
|
return simulate_tree(
|
|
76
|
-
events=
|
|
77
|
-
mutations=mutations,
|
|
76
|
+
events=events,
|
|
78
77
|
min_tips=integer(self.min_tips, data),
|
|
79
78
|
max_tips=None if self.max_tips is None else integer(self.max_tips, data),
|
|
80
79
|
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,22 @@ 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
|
+
|
|
19
|
+
|
|
10
20
|
class Event(ABC):
|
|
21
|
+
type: EventType
|
|
22
|
+
|
|
11
23
|
def __init__(self, state: str, rate: SkylineParameterLike):
|
|
12
24
|
self.state = state
|
|
13
25
|
self.rate = skyline_parameter(rate)
|
|
26
|
+
if any(v < 0 for v in self.rate.value):
|
|
27
|
+
raise ValueError("Event rates must be non-negative.")
|
|
14
28
|
|
|
15
29
|
def draw_individual(self, model: Model, rng: Generator) -> int:
|
|
16
30
|
return rng.choice(model.get_population(self.state))
|
|
@@ -23,4 +37,6 @@ class Event(ABC):
|
|
|
23
37
|
return rate * n_individuals
|
|
24
38
|
|
|
25
39
|
@abstractmethod
|
|
26
|
-
def apply(
|
|
40
|
+
def apply(
|
|
41
|
+
self, model: Model, events: "list[Event]", time: float, rng: Generator
|
|
42
|
+
) -> 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
|
|
17
|
+
def get_CT_state(state: str) -> str:
|
|
17
18
|
return f"{state}{CT_POSTFIX}"
|
|
18
19
|
|
|
19
20
|
|
|
20
|
-
def
|
|
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
|
|
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)
|
|
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
|
|
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)
|
|
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
|
|
65
|
-
model.migrate(contact,
|
|
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(
|
|
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(
|
|
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,
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
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)
|
|
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)
|
|
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
|
|
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,27 +35,19 @@ def get_mutation_id(node_name: str) -> int:
|
|
|
40
35
|
return 0
|
|
41
36
|
|
|
42
37
|
|
|
43
|
-
class
|
|
44
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
state: str,
|
|
44
|
+
rate: SkylineParameterLike,
|
|
45
|
+
rate_scalers: dict[EventType, Distribution],
|
|
46
|
+
rates_to_log: list[EventType] | None = None,
|
|
47
|
+
):
|
|
48
|
+
super().__init__(state, rate)
|
|
63
49
|
self.rate_scalers = rate_scalers
|
|
50
|
+
self.rates_to_log = [] if rates_to_log is None else rates_to_log
|
|
64
51
|
|
|
65
52
|
def apply(
|
|
66
53
|
self, model: Model, events: list[Event], time: float, rng: Generator
|
|
@@ -70,11 +57,10 @@ class Mutation:
|
|
|
70
57
|
model.context[NEXT_MUTATION_ID] += 1
|
|
71
58
|
mutation_id = model.context[NEXT_MUTATION_ID]
|
|
72
59
|
|
|
73
|
-
individual =
|
|
74
|
-
|
|
75
|
-
model.migrate(individual, _get_mutated_state(mutation_id, state), time)
|
|
60
|
+
individual = self.draw_individual(model, rng)
|
|
61
|
+
model.migrate(individual, _get_mutated_state(mutation_id, self.state), time)
|
|
76
62
|
|
|
77
|
-
rate_scalers: dict[
|
|
63
|
+
rate_scalers: dict[EventType, float] = {
|
|
78
64
|
target_type: getattr(rng, rate_scaler.type)(**rate_scaler.args)
|
|
79
65
|
for target_type, rate_scaler in self.rate_scalers.items()
|
|
80
66
|
}
|
|
@@ -83,22 +69,30 @@ class Mutation:
|
|
|
83
69
|
for event in [
|
|
84
70
|
deepcopy(e)
|
|
85
71
|
for e in events
|
|
86
|
-
if _get_mutation(state) == _get_mutation(e.state)
|
|
72
|
+
if _get_mutation(self.state) == _get_mutation(e.state)
|
|
87
73
|
]:
|
|
88
74
|
event.state = _get_mutated_state(mutation_id, event.state)
|
|
75
|
+
|
|
89
76
|
if isinstance(event, Birth | BirthWithContactTracing):
|
|
90
77
|
event.child_state = _get_mutated_state(mutation_id, event.child_state)
|
|
78
|
+
metadata_key = f"birth_rate_from_{event.state}_to_{event.child_state}"
|
|
91
79
|
elif isinstance(event, Migration):
|
|
92
80
|
event.target_state = _get_mutated_state(mutation_id, event.target_state)
|
|
93
|
-
|
|
81
|
+
metadata_key = (
|
|
82
|
+
f"migration_rate_from_{event.state}_to_{event.target_state}"
|
|
83
|
+
)
|
|
84
|
+
elif isinstance(
|
|
85
|
+
event, Mutation | Death | Sampling | SamplingWithContactTracing
|
|
86
|
+
):
|
|
87
|
+
metadata_key = f"{event.type}_rate_for_{event.state}"
|
|
88
|
+
else:
|
|
94
89
|
raise ValueError(
|
|
95
90
|
f"Mutation not implemented for event of type {type(event)}."
|
|
96
91
|
)
|
|
97
92
|
|
|
98
|
-
|
|
99
|
-
if
|
|
100
|
-
|
|
101
|
-
metadata[f"{MUTATION_PREFIX}{mutation_id}.{target_type}.rate.value"] = (
|
|
93
|
+
event.rate *= rate_scalers.get(event.type, 1)
|
|
94
|
+
if event.type in self.rates_to_log:
|
|
95
|
+
metadata[metadata_key] = (
|
|
102
96
|
event.rate.value[0]
|
|
103
97
|
if len(event.rate.value) == 1
|
|
104
98
|
else list(event.rate.value)
|
|
@@ -109,4 +103,4 @@ class Mutation:
|
|
|
109
103
|
return metadata
|
|
110
104
|
|
|
111
105
|
def __repr__(self) -> str:
|
|
112
|
-
return f"Mutation(rate={self.rate}, rate_scalers={self.rate_scalers})"
|
|
106
|
+
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if
|
|
102
|
-
|
|
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,11 +1,11 @@
|
|
|
1
|
-
phylogenie/__init__.py,sha256=
|
|
1
|
+
phylogenie/__init__.py,sha256=FdNHTw1U1eYDz8w_EOvk9R0rdOXvCMAgbm7ilXs6Fog,2861
|
|
2
2
|
phylogenie/draw.py,sha256=37eQDK8TKmL8bxSzXvi7jNvrR7ulj7MByR5MA1FtyyE,5317
|
|
3
3
|
phylogenie/generators/__init__.py,sha256=zsOxy28-9j9alOQLIgrOAFfmM58NNHO_NEtW-KXQXAY,888
|
|
4
4
|
phylogenie/generators/alisim.py,sha256=1YQLpOG_Bpn9YqExQqEu-wz1MDGCbpPmTdhrBb6TbWc,2820
|
|
5
|
-
phylogenie/generators/configs.py,sha256=
|
|
5
|
+
phylogenie/generators/configs.py,sha256=zjNnljBBC-d6DTjWjieE8-HnKeE58LAy3moBYiD6NFo,1042
|
|
6
6
|
phylogenie/generators/dataset.py,sha256=kY92diePr2IjiLejHLixJoYRc-2LpM-GBt3wkX9SYvA,2109
|
|
7
|
-
phylogenie/generators/factories.py,sha256=
|
|
8
|
-
phylogenie/generators/trees.py,sha256=
|
|
7
|
+
phylogenie/generators/factories.py,sha256=qSz-s7ltQwUf7R6TPZ-5xFyOnnSHkKn0u5IukeysXVU,8544
|
|
8
|
+
phylogenie/generators/trees.py,sha256=zsmL3_YUUwtn-_NrGSvwZVSGIm9kpf8SPHxoQCIT_hE,10168
|
|
9
9
|
phylogenie/generators/typeguards.py,sha256=yj4VkhOaUXJ2OrY-6zhOeY9C4yKIQxjZtk2d-vIxttQ,828
|
|
10
10
|
phylogenie/io/__init__.py,sha256=gtRYtDdZSTlWCj3I4vmMJSAs93jdz5RySkCakD3sxlQ,214
|
|
11
11
|
phylogenie/io/fasta.py,sha256=IWtNb_RQLR6kvS0G826wB9SodkCGfugddoUHx78Yrec,837
|
|
@@ -20,20 +20,20 @@ phylogenie/skyline/matrix.py,sha256=v4SitY7VbXprqlqQckjWTzW5hwRmCyIF595R6IJMxWw,
|
|
|
20
20
|
phylogenie/skyline/parameter.py,sha256=TVqkqirGXNN-VP8hnIJACPkOxUan6LkGa5o_JcPfwbY,4834
|
|
21
21
|
phylogenie/skyline/vector.py,sha256=60jtp7PieiEaEH0Tp6zNjNKjyzpN_nT5uwBUXbgeATk,7261
|
|
22
22
|
phylogenie/tree.py,sha256=P1uM6s32TsODpvNJQIPMix9oj39vGSw_wsHYp2wmy5U,5246
|
|
23
|
-
phylogenie/treesimulator/__init__.py,sha256=
|
|
24
|
-
phylogenie/treesimulator/events/__init__.py,sha256=
|
|
25
|
-
phylogenie/treesimulator/events/base.py,sha256=
|
|
26
|
-
phylogenie/treesimulator/events/contact_tracing.py,sha256=
|
|
27
|
-
phylogenie/treesimulator/events/core.py,sha256=
|
|
28
|
-
phylogenie/treesimulator/
|
|
29
|
-
phylogenie/treesimulator/
|
|
23
|
+
phylogenie/treesimulator/__init__.py,sha256=0wU1PECAcM5rBg5MmeA3poWgfGDzqHySmSJjJs1wkPw,1088
|
|
24
|
+
phylogenie/treesimulator/events/__init__.py,sha256=w2tJ0D2WB5AiCbr3CsKN6vdADueiAEMzd_ve0rpa4zg,939
|
|
25
|
+
phylogenie/treesimulator/events/base.py,sha256=Rb9qO0EURKGbFPv_KuRrzZv5Krl1ZEMj8ASHXlBLhbI,1259
|
|
26
|
+
phylogenie/treesimulator/events/contact_tracing.py,sha256=QqinLfQmBBi6qF3nFE_Qsiyd6RUG0RJqxiBOKK0nRGE,5196
|
|
27
|
+
phylogenie/treesimulator/events/core.py,sha256=bhgQgi5L-oaHsoWJmUOsTTzWxi0POYxVLoF-KrC8AGQ,8179
|
|
28
|
+
phylogenie/treesimulator/events/mutations.py,sha256=fGxxeXhiFrMAaS-s_vd42aVBZ6XQiRjpW9uJUATNk1E,3752
|
|
29
|
+
phylogenie/treesimulator/features.py,sha256=nNfv0kPudAZnrnufIVuFCR_VEJSP-6fZ2L-VG5_-Fqo,1366
|
|
30
|
+
phylogenie/treesimulator/gillespie.py,sha256=_O1TqK-LOjH6k8ZSos-CbVQ7wJZPzf1Awv_1DXt0NYE,5730
|
|
30
31
|
phylogenie/treesimulator/model.py,sha256=lhDwmBFQ8Qh8qVGZPgED0vehtPC3DE7_CgCV_8rPB-A,4641
|
|
31
|
-
phylogenie/treesimulator/mutations.py,sha256=Wv0qJqjGAMNTYftiCCaSXfZQA6pj8pSrmrOLyTI86ko,3635
|
|
32
32
|
phylogenie/typeguards.py,sha256=JtqmbEWJZBRHbWgCvcl6nrWm3VcBfzRbklbTBYHItn0,1325
|
|
33
33
|
phylogenie/typings.py,sha256=p694PBe_tk25A6N8vGGWxuqoDtt3nHFUsIYJrwR_76Y,494
|
|
34
34
|
phylogenie/utils.py,sha256=ehVk_2kvjW8Q_EyM2kxBPHYiK-KlPmZQx7JeVN6Fh-E,5419
|
|
35
|
-
phylogenie-2.1.
|
|
36
|
-
phylogenie-2.1.
|
|
37
|
-
phylogenie-2.1.
|
|
38
|
-
phylogenie-2.1.
|
|
39
|
-
phylogenie-2.1.
|
|
35
|
+
phylogenie-2.1.27.dist-info/LICENSE.txt,sha256=NUrDqElK-eD3I0WqC004CJsy6cs0JgsAoebDv_42-pw,1071
|
|
36
|
+
phylogenie-2.1.27.dist-info/METADATA,sha256=UUZTU3YJ5yrAzQZ8EiiVsTmhuZzKNiGU_roQazkf3Mo,5477
|
|
37
|
+
phylogenie-2.1.27.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
38
|
+
phylogenie-2.1.27.dist-info/entry_points.txt,sha256=Rt6_usN0FkBX1ZfiqCirjMN9FKOgFLG8rydcQ8kugeE,51
|
|
39
|
+
phylogenie-2.1.27.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|