phylogenie 2.1.0__py3-none-any.whl → 2.1.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,106 +1,69 @@
1
- from abc import ABC, abstractmethod
2
- from collections.abc import Sequence
3
- from dataclasses import dataclass
4
-
5
- import numpy as np
6
1
  from numpy.random import Generator
7
2
 
8
3
  from phylogenie.skyline import (
9
4
  SkylineMatrixCoercible,
10
- SkylineParameter,
11
5
  SkylineParameterLike,
12
6
  SkylineVectorCoercible,
13
7
  skyline_matrix,
14
- skyline_parameter,
15
8
  skyline_vector,
16
9
  )
17
- from phylogenie.treesimulator.model import Model
10
+ from phylogenie.treesimulator.model import Event, Model
18
11
 
19
12
  INFECTIOUS_STATE = "I"
20
13
  EXPOSED_STATE = "E"
21
14
  SUPERSPREADER_STATE = "S"
22
- CT_POSTFIX = "-CT"
23
-
24
-
25
- def _get_CT_state(state: str) -> str:
26
- return f"{state}{CT_POSTFIX}"
27
-
28
-
29
- def _is_CT_state(state: str) -> bool:
30
- return state.endswith(CT_POSTFIX)
31
-
32
15
 
33
- @dataclass
34
- class Event(ABC):
35
- rate: SkylineParameter
36
- state: str
37
16
 
38
- def draw_individual(self, model: Model, rng: Generator) -> int:
39
- return rng.choice(model.get_population(self.state))
40
-
41
- def get_propensity(self, model: Model, time: float) -> float:
42
- n_individuals = model.count_individuals(self.state)
43
- rate = self.rate.get_value_at_time(time)
44
- if rate == np.inf and not n_individuals:
45
- return 0
46
- return rate * n_individuals
47
-
48
- @abstractmethod
49
- def apply(self, model: Model, time: float, rng: Generator) -> None: ...
50
-
51
-
52
- @dataclass
53
17
  class Birth(Event):
54
- child_state: str
18
+ def __init__(self, state: str, rate: SkylineParameterLike, child_state: str):
19
+ super().__init__(state, rate)
20
+ self.child_state = child_state
55
21
 
56
22
  def apply(self, model: Model, time: float, rng: Generator) -> None:
57
23
  individual = self.draw_individual(model, rng)
58
24
  model.birth_from(individual, self.child_state, time)
59
25
 
26
+ def __repr__(self) -> str:
27
+ return f"Birth(state={self.state}, rate={self.rate}, child_state={self.child_state})"
28
+
60
29
 
61
30
  class Death(Event):
62
31
  def apply(self, model: Model, time: float, rng: Generator) -> None:
63
32
  individual = self.draw_individual(model, rng)
64
33
  model.remove(individual, time)
65
34
 
35
+ def __repr__(self) -> str:
36
+ return f"Death(state={self.state}, rate={self.rate})"
37
+
66
38
 
67
- @dataclass
68
39
  class Migration(Event):
69
- target_state: str
40
+ def __init__(self, state: str, rate: SkylineParameterLike, target_state: str):
41
+ super().__init__(state, rate)
42
+ self.target_state = target_state
70
43
 
71
44
  def apply(self, model: Model, time: float, rng: Generator) -> None:
72
45
  individual = self.draw_individual(model, rng)
73
46
  model.migrate(individual, self.target_state, time)
74
47
 
48
+ def __repr__(self) -> str:
49
+ return f"Migration(state={self.state}, rate={self.rate}, target_state={self.target_state})"
50
+
75
51
 
76
- @dataclass
77
52
  class Sampling(Event):
78
- removal: bool
53
+ def __init__(self, state: str, rate: SkylineParameterLike, removal: bool):
54
+ super().__init__(state, rate)
55
+ self.removal = removal
79
56
 
80
57
  def apply(self, model: Model, time: float, rng: Generator) -> None:
81
58
  individual = self.draw_individual(model, rng)
82
59
  model.sample(individual, time, self.removal)
83
60
 
84
-
85
- @dataclass
86
- class SamplingWithContactTracing(Event):
87
- max_notified_contacts: int
88
- notification_probability: SkylineParameter
89
-
90
- def apply(self, model: Model, time: float, rng: Generator) -> None:
91
- individual = self.draw_individual(model, rng)
92
- model.sample(individual, time, True)
93
- population = model.get_population()
94
- for contact in model.get_lineage(individual)[-self.max_notified_contacts :]:
95
- if contact in population:
96
- state = model.get_state(contact)
97
- p = self.notification_probability.get_value_at_time(time)
98
- if not _is_CT_state(state) and rng.random() < p:
99
- model.migrate(contact, _get_CT_state(state), time)
61
+ def __repr__(self) -> str:
62
+ return f"Sampling(state={self.state}, rate={self.rate}, removal={self.removal})"
100
63
 
101
64
 
102
65
  def get_canonical_events(
103
- states: Sequence[str],
66
+ states: list[str],
104
67
  sampling_rates: SkylineVectorCoercible,
105
68
  remove_after_sampling: bool,
106
69
  birth_rates: SkylineVectorCoercible = 0,
@@ -116,27 +79,27 @@ def get_canonical_events(
116
79
 
117
80
  events: list[Event] = []
118
81
  for i, state in enumerate(states):
119
- events.append(Birth(birth_rates[i], state, state))
120
- events.append(Death(death_rates[i], state))
121
- events.append(Sampling(sampling_rates[i], state, remove_after_sampling))
82
+ events.append(Birth(state, birth_rates[i], state))
83
+ events.append(Death(state, death_rates[i]))
84
+ events.append(Sampling(state, sampling_rates[i], remove_after_sampling))
122
85
 
123
86
  if migration_rates is not None:
124
87
  migration_rates = skyline_matrix(migration_rates, N, N - 1)
125
88
  for i, state in enumerate(states):
126
89
  for j, other_state in enumerate([s for s in states if s != state]):
127
- events.append(Migration(migration_rates[i, j], state, other_state))
90
+ events.append(Migration(state, migration_rates[i, j], other_state))
128
91
 
129
92
  if birth_rates_among_states is not None:
130
93
  birth_rates_among_states = skyline_matrix(birth_rates_among_states, N, N - 1)
131
94
  for i, state in enumerate(states):
132
95
  for j, other_state in enumerate([s for s in states if s != state]):
133
- events.append(Birth(birth_rates_among_states[i, j], state, other_state))
96
+ events.append(Birth(state, birth_rates_among_states[i, j], other_state))
134
97
 
135
98
  return [event for event in events if event.rate]
136
99
 
137
100
 
138
101
  def get_FBD_events(
139
- states: Sequence[str],
102
+ states: list[str],
140
103
  sampling_proportions: SkylineVectorCoercible = 1,
141
104
  diversification: SkylineVectorCoercible = 0,
142
105
  turnover: SkylineVectorCoercible = 0,
@@ -170,7 +133,7 @@ def get_FBD_events(
170
133
 
171
134
 
172
135
  def get_epidemiological_events(
173
- states: Sequence[str],
136
+ states: list[str],
174
137
  sampling_proportions: SkylineVectorCoercible = 1,
175
138
  reproduction_numbers: SkylineVectorCoercible = 0,
176
139
  become_uninfectious_rates: SkylineVectorCoercible = 0,
@@ -254,56 +217,3 @@ def get_BDSS_events(
254
217
  become_uninfectious_rates=1 / infectious_period,
255
218
  sampling_proportions=sampling_proportion,
256
219
  )
257
-
258
-
259
- def get_contact_tracing_events(
260
- events: Sequence[Event],
261
- max_notified_contacts: int = 1,
262
- notification_probability: SkylineParameterLike = 1,
263
- sampling_rate_after_notification: SkylineParameterLike = np.inf,
264
- samplable_states_after_notification: Sequence[str] | None = None,
265
- ) -> list[Event]:
266
- ct_events = [e for e in events if not isinstance(e, Sampling)]
267
-
268
- for event in events:
269
- if isinstance(event, Migration):
270
- ct_events.append(
271
- Migration(
272
- event.rate,
273
- _get_CT_state(event.state),
274
- _get_CT_state(event.target_state),
275
- )
276
- )
277
- elif isinstance(event, Birth):
278
- ct_events.append(
279
- Birth(event.rate, _get_CT_state(event.state), event.child_state)
280
- )
281
- elif isinstance(event, Sampling):
282
- if not event.removal:
283
- raise ValueError(
284
- "Contact tracing requires removal to be set for all sampling events."
285
- )
286
- ct_events.append(
287
- SamplingWithContactTracing(
288
- event.rate,
289
- event.state,
290
- max_notified_contacts,
291
- skyline_parameter(notification_probability),
292
- )
293
- )
294
-
295
- for state in (
296
- samplable_states_after_notification
297
- if samplable_states_after_notification is not None
298
- else {e.state for e in events}
299
- ):
300
- ct_events.append(
301
- SamplingWithContactTracing(
302
- skyline_parameter(sampling_rate_after_notification),
303
- _get_CT_state(state),
304
- max_notified_contacts,
305
- skyline_parameter(notification_probability),
306
- )
307
- )
308
-
309
- return ct_events
@@ -0,0 +1,105 @@
1
+ from copy import deepcopy
2
+ from enum import Enum
3
+ from typing import Type
4
+
5
+ from numpy.random import Generator
6
+
7
+ from phylogenie.skyline import SkylineParameterLike
8
+ from phylogenie.treesimulator.events.contact_tracing import (
9
+ BirthWithContactTracing,
10
+ SamplingWithContactTracing,
11
+ )
12
+ from phylogenie.treesimulator.events.core import (
13
+ Birth,
14
+ Death,
15
+ Event,
16
+ Migration,
17
+ Sampling,
18
+ )
19
+ from phylogenie.treesimulator.model import Model
20
+ from phylogenie.utils import Distribution
21
+
22
+ MUTATION_PREFIX = "MUT-"
23
+ MUTATIONS_KEY = "MUTATIONS"
24
+
25
+
26
+ def _get_mutation(state: str) -> str | None:
27
+ return state.split(".")[0] if state.startswith(MUTATION_PREFIX) else None
28
+
29
+
30
+ def _get_mutated_state(mutation_id: int, state: str) -> str:
31
+ if state.startswith(MUTATION_PREFIX):
32
+ state = state.split(".")[1]
33
+ return f"{MUTATION_PREFIX}{mutation_id}.{state}"
34
+
35
+
36
+ class TargetType(str, Enum):
37
+ BIRTH = "birth"
38
+ DEATH = "death"
39
+ MIGRATION = "migration"
40
+ SAMPLING = "sampling"
41
+ MUTATION = "mutation"
42
+
43
+
44
+ class Mutation(Event):
45
+ def __init__(
46
+ self,
47
+ state: str,
48
+ rate: SkylineParameterLike,
49
+ rate_scalers: dict[TargetType, Distribution],
50
+ ):
51
+ super().__init__(state, rate)
52
+ self.rate_scalers = rate_scalers
53
+
54
+ def apply(self, model: Model, time: float, rng: Generator) -> None:
55
+ if MUTATIONS_KEY not in model.context:
56
+ model.context[MUTATIONS_KEY] = 0
57
+ model.context[MUTATIONS_KEY] += 1
58
+ mutation_id = model.context[MUTATIONS_KEY]
59
+
60
+ individual = self.draw_individual(model, rng)
61
+ model.migrate(individual, _get_mutated_state(mutation_id, self.state), time)
62
+
63
+ rate_scalers = {
64
+ target_type: getattr(rng, rate_scaler.type)(**rate_scaler.args)
65
+ for target_type, rate_scaler in self.rate_scalers.items()
66
+ }
67
+
68
+ for event in [
69
+ deepcopy(e)
70
+ for e in model.events
71
+ if _get_mutation(self.state) == _get_mutation(e.state)
72
+ ]:
73
+ event.state = _get_mutated_state(mutation_id, event.state)
74
+ if isinstance(event, Birth | BirthWithContactTracing):
75
+ event.child_state = _get_mutated_state(mutation_id, event.child_state)
76
+ elif isinstance(event, Migration):
77
+ event.target_state = _get_mutated_state(mutation_id, event.target_state)
78
+ elif not isinstance(
79
+ event, Mutation | Death | Sampling | SamplingWithContactTracing
80
+ ):
81
+ raise ValueError(
82
+ f"Mutation not defined for event of type {type(event)}."
83
+ )
84
+
85
+ for target_type, rate_scaler in rate_scalers.items():
86
+ if target_type not in TARGETS:
87
+ raise ValueError(
88
+ f"Unsupported target type {target_type} for mutation."
89
+ )
90
+ if isinstance(event, TARGETS[target_type]):
91
+ event.rate *= rate_scaler
92
+
93
+ model.add_event(event)
94
+
95
+ def __repr__(self) -> str:
96
+ return f"Mutation(state={self.state}, rate={self.rate}, rate_scalers={self.rate_scalers})"
97
+
98
+
99
+ TARGETS: dict[TargetType, tuple[Type[Event], ...]] = {
100
+ TargetType.BIRTH: (Birth, BirthWithContactTracing),
101
+ TargetType.DEATH: (Death,),
102
+ TargetType.MIGRATION: (Migration,),
103
+ TargetType.SAMPLING: (Sampling, SamplingWithContactTracing),
104
+ TargetType.MUTATION: (Mutation,),
105
+ }
@@ -8,21 +8,21 @@ from tqdm import tqdm
8
8
 
9
9
  from phylogenie.io import dump_newick
10
10
  from phylogenie.tree import Tree
11
- from phylogenie.treesimulator.events import Event
12
- from phylogenie.treesimulator.model import Model
11
+ from phylogenie.treesimulator.model import Event, Model
12
+
13
+ MAX_TIPS = 2**32
13
14
 
14
15
 
15
16
  def simulate_tree(
16
17
  events: Sequence[Event],
17
18
  min_tips: int = 1,
18
- max_tips: int = 2**32,
19
+ max_tips: int = MAX_TIPS,
19
20
  max_time: float = np.inf,
20
21
  init_state: str | None = None,
21
22
  sampling_probability_at_present: float = 0.0,
22
- max_tries: int | None = None,
23
23
  seed: int | None = None,
24
24
  ) -> Tree | None:
25
- if max_time == np.inf and max_tips == 2**32:
25
+ if max_time == np.inf and max_tips == MAX_TIPS:
26
26
  raise ValueError("Either max_time or max_tips must be specified.")
27
27
 
28
28
  if max_time == np.inf and sampling_probability_at_present:
@@ -30,7 +30,7 @@ def simulate_tree(
30
30
  "sampling_probability_at_present cannot be set when max_time is infinite."
31
31
  )
32
32
 
33
- states = {e.state for e in events}
33
+ states = {e.state for e in events if e.state}
34
34
  if init_state is None and len(states) > 1:
35
35
  raise ValueError(
36
36
  "Init state must be provided for models with more than one state."
@@ -41,15 +41,15 @@ def simulate_tree(
41
41
  raise ValueError(f"Init state {init_state} not found in event states: {states}")
42
42
 
43
43
  rng = default_rng(seed)
44
- n_tries = 0
45
- while max_tries is None or n_tries < max_tries:
46
- model = Model(init_state)
44
+ while True:
45
+ model = Model(init_state, events)
47
46
  current_time = 0.0
48
47
  change_times = sorted(set(t for e in events for t in e.rate.change_times))
49
48
  next_change_time = change_times.pop(0) if change_times else np.inf
50
49
 
51
50
  target_n_tips = rng.integers(min_tips, max_tips) if max_time == np.inf else None
52
51
  while current_time < max_time:
52
+ events = model.events
53
53
  rates = [e.get_propensity(model, current_time) for e in events]
54
54
 
55
55
  instantaneous_events = [e for e, r in zip(events, rates) if r == np.inf]
@@ -85,21 +85,17 @@ def simulate_tree(
85
85
 
86
86
  if min_tips <= model.n_sampled <= max_tips:
87
87
  return model.get_sampled_tree()
88
- n_tries += 1
89
-
90
- print("WARNING: Maximum number of tries reached, returning None.")
91
88
 
92
89
 
93
90
  def generate_trees(
94
91
  output_dir: str,
95
92
  n_trees: int,
96
- events: Sequence[Event],
93
+ events: list[Event],
97
94
  min_tips: int = 1,
98
95
  max_tips: int = 2**32,
99
96
  max_time: float = np.inf,
100
97
  init_state: str | None = None,
101
98
  sampling_probability_at_present: float = 0.0,
102
- max_tries: int | None = None,
103
99
  seed: int | None = None,
104
100
  n_jobs: int = -1,
105
101
  ) -> None:
@@ -116,7 +112,6 @@ def generate_trees(
116
112
  max_time=max_time,
117
113
  init_state=init_state,
118
114
  sampling_probability_at_present=sampling_probability_at_present,
119
- max_tries=max_tries,
120
115
  seed=int(rng.integers(2**32)),
121
116
  )
122
117
  for _ in range(n_trees)
@@ -1,9 +1,35 @@
1
+ from abc import ABC, abstractmethod
1
2
  from collections import defaultdict
3
+ from collections.abc import Sequence
2
4
  from dataclasses import dataclass
5
+ from typing import Any
3
6
 
7
+ import numpy as np
8
+ from numpy.random import Generator
9
+
10
+ from phylogenie.skyline import SkylineParameterLike, skyline_parameter
4
11
  from phylogenie.tree import Tree
5
12
 
6
13
 
14
+ class Event(ABC):
15
+ def __init__(self, state: str, rate: SkylineParameterLike):
16
+ self.state = state
17
+ self.rate = skyline_parameter(rate)
18
+
19
+ def draw_individual(self, model: "Model", rng: Generator) -> int:
20
+ return rng.choice(model.get_population(self.state))
21
+
22
+ def get_propensity(self, model: "Model", time: float) -> float:
23
+ n_individuals = model.count_individuals(self.state)
24
+ rate = self.rate.get_value_at_time(time)
25
+ if rate == np.inf and not n_individuals:
26
+ return 0
27
+ return rate * n_individuals
28
+
29
+ @abstractmethod
30
+ def apply(self, model: "Model", time: float, rng: Generator) -> None: ...
31
+
32
+
7
33
  @dataclass
8
34
  class Individual:
9
35
  id: int
@@ -12,19 +38,27 @@ class Individual:
12
38
 
13
39
 
14
40
  class Model:
15
- def __init__(self, init_state: str):
41
+ def __init__(self, init_state: str, events: Sequence[Event]):
16
42
  self._next_node_id = 0
17
43
  self._next_individual_id = 0
18
44
  self._population: dict[int, Individual] = {}
19
45
  self._states: dict[str, set[int]] = defaultdict(set)
20
- self._lineages: dict[int, list[Individual]] = defaultdict(list)
21
46
  self._sampled: set[str] = set()
22
47
  self._tree = self._get_new_individual(init_state).node
48
+ self._events = list(events)
49
+ self.context: dict[str, Any] = {}
23
50
 
24
51
  @property
25
52
  def n_sampled(self) -> int:
26
53
  return len(self._sampled)
27
54
 
55
+ @property
56
+ def events(self) -> tuple[Event, ...]:
57
+ return tuple(self._events)
58
+
59
+ def add_event(self, event: Event) -> None:
60
+ self._events.append(event)
61
+
28
62
  def _get_new_node(self, state: str) -> Tree:
29
63
  self._next_node_id += 1
30
64
  return Tree(f"{self._next_node_id}|{state}")
@@ -54,9 +88,8 @@ class Model:
54
88
  def remove(self, id: int, time: float) -> None:
55
89
  individual = self._population[id]
56
90
  self._set_branch_length(individual.node, time)
57
- state = individual.state
58
91
  self._population.pop(id)
59
- self._states[state].remove(id)
92
+ self._states[individual.state].remove(id)
60
93
 
61
94
  def migrate(self, id: int, state: str, time: float) -> None:
62
95
  individual = self._population[id]
@@ -65,13 +98,12 @@ class Model:
65
98
  self._states[state].add(id)
66
99
  self._stem(individual, time)
67
100
 
68
- def birth_from(self, id: int, state: str, time: float) -> None:
101
+ def birth_from(self, id: int, state: str, time: float) -> int:
69
102
  individual = self._population[id]
70
103
  new_individual = self._get_new_individual(state)
71
104
  individual.node.add_child(new_individual.node)
72
105
  self._stem(individual, time)
73
- self._lineages[id].append(new_individual)
74
- self._lineages[new_individual.id].append(individual)
106
+ return new_individual.id
75
107
 
76
108
  def sample(self, id: int, time: float, removal: bool) -> None:
77
109
  individual = self._population[id]
@@ -85,9 +117,6 @@ class Model:
85
117
  individual.node.add_child(sample_node)
86
118
  self._stem(individual, time)
87
119
 
88
- def get_lineage(self, id: int) -> list[int]:
89
- return [individual.id for individual in self._lineages[id]]
90
-
91
120
  def get_state(self, id: int) -> str:
92
121
  return self._population[id].state
93
122
 
@@ -98,29 +127,28 @@ class Model:
98
127
  if node.parent is None:
99
128
  raise ValueError("No samples in the tree.")
100
129
  else:
101
- node.parent.children.remove(node)
130
+ node.parent.remove_child(node)
102
131
  elif len(node.children) == 1:
103
132
  (child,) = node.children
104
- child.parent = node.parent
133
+ child.set_parent(node.parent)
105
134
  assert child.branch_length is not None
106
135
  assert node.branch_length is not None
107
136
  child.branch_length += node.branch_length
108
137
  if node.parent is None:
109
138
  return child
110
139
  else:
111
- node.parent.children.append(child)
112
- node.parent.children.remove(node)
140
+ node.parent.remove_child(node)
113
141
  return tree
114
142
 
115
143
  def get_full_tree(self) -> Tree:
116
144
  return self._tree.copy()
117
145
 
118
- def get_population(self, state: str | None = None) -> list[int]:
119
- if state is None:
146
+ def get_population(self, states: str | None = None) -> list[int]:
147
+ if states is None:
120
148
  return list(self._population)
121
- return list(self._states[state])
149
+ return list(self._states[states])
122
150
 
123
- def count_individuals(self, state: str | None = None) -> int:
124
- if state is None:
151
+ def count_individuals(self, states: str | None = None) -> int:
152
+ if states is None:
125
153
  return len(self._population)
126
- return len(self._states[state])
154
+ return len(self._states[states])
phylogenie/typings.py CHANGED
@@ -16,6 +16,5 @@ Many2DScalars = Many2D[Scalar]
16
16
  Many3DScalars = Many3D[Scalar]
17
17
 
18
18
  Vector1D = list[Scalar]
19
- IntVector1D = list[int]
20
19
  Vector2D = list[Vector1D]
21
20
  Vector3D = list[Vector2D]
phylogenie/utils.py ADDED
@@ -0,0 +1,17 @@
1
+ from typing import Any
2
+
3
+ from pydantic import BaseModel, ConfigDict
4
+
5
+
6
+ class StrictBaseModel(BaseModel):
7
+ model_config = ConfigDict(extra="forbid")
8
+
9
+
10
+ class Distribution(BaseModel):
11
+ type: str
12
+ model_config = ConfigDict(extra="allow")
13
+
14
+ @property
15
+ def args(self) -> dict[str, Any]:
16
+ assert self.model_extra is not None
17
+ return self.model_extra
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: phylogenie
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: Generate phylogenetic datasets with minimal setup effort
5
5
  Author: Gabriele Marino
6
6
  Author-email: gabmarino.8601@gmail.com
@@ -24,7 +24,6 @@ Description-Content-Type: text/markdown
24
24
 
25
25
  [![AliSim](https://img.shields.io/badge/Powered%20by-AliSim-orange?style=flat-square)](https://iqtree.github.io/doc/AliSim)
26
26
  [![PyPI version](https://img.shields.io/pypi/v/phylogenie)](https://pypi.org/project/phylogenie/)
27
- [![PyPI downloads](https://shields.io/pypi/dm/phylogenie)](https://pypi.org/project/phylogenie/)
28
27
 
29
28
  Phylogenie is a [Python](https://www.python.org/) package designed to easily simulate phylogenetic datasets—such as trees and multiple sequence alignments (MSAs)—with minimal setup effort. Simply specify the distributions from which your parameters should be sampled, and Phylogenie will handle the rest!
30
29
 
@@ -0,0 +1,32 @@
1
+ phylogenie/__init__.py,sha256=T2mRLsYtoLlWt8GlxrrUnfXJ9XVioq7hTvVq3uJpwQI,2215
2
+ phylogenie/generators/__init__.py,sha256=zsOxy28-9j9alOQLIgrOAFfmM58NNHO_NEtW-KXQXAY,888
3
+ phylogenie/generators/alisim.py,sha256=dDqlSwLDbRE2u5SZlsq1mArobTBtuk0aeXY3m1N-bWA,2374
4
+ phylogenie/generators/configs.py,sha256=AiiFS6rpH9BPwDKCkT4SVrRzfLFFrwRCJM4CRj0Srdk,1072
5
+ phylogenie/generators/dataset.py,sha256=wYtb5fxjrM0bD9KVDlgZPpiR4ACezdKKfsmsXyc4__0,2755
6
+ phylogenie/generators/factories.py,sha256=14p1wvJtjdXgM4mPqV9vX34HYcKpemx9TPfemvoB5Wo,7265
7
+ phylogenie/generators/trees.py,sha256=EEj5KPuu7FSOoti_gGTmgWgiX68rEKFh57Hp6BDw8po,10049
8
+ phylogenie/generators/typeguards.py,sha256=yj4VkhOaUXJ2OrY-6zhOeY9C4yKIQxjZtk2d-vIxttQ,828
9
+ phylogenie/io.py,sha256=y7nQIvLgCvqELsXFKfm1GgKJO_saoQ-7zQpE3Kvajzc,3509
10
+ phylogenie/main.py,sha256=vtvSpQxBNlYABoFQ25czl-l3fIr4QRo3svWVd-jcArw,1170
11
+ phylogenie/msa.py,sha256=JDGyZUsAq6-m-SQjoCDjAkAZIxfgyl_PDIhdYn5HOow,2064
12
+ phylogenie/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ phylogenie/skyline/__init__.py,sha256=7pF4CUb4ZCLzNYJNhOjpuTOLTRhlK7L6ugfccNqjIGo,620
14
+ phylogenie/skyline/matrix.py,sha256=Gl8OgKjtieG0NwPYiPimKI36gefV8fm_OeorjdXxPTs,9146
15
+ phylogenie/skyline/parameter.py,sha256=EM9qlPt0JhMBy3TbztM0dj24BaGNEy8KWKdTObDKhbI,4644
16
+ phylogenie/skyline/vector.py,sha256=bJP7_FNX_Klt6wXqsyfj0KX3VNj6-dIhzCKSJuQcOV0,7115
17
+ phylogenie/tree.py,sha256=Cum74mTdmgfGXk25dnvUngr4zDYRyWFq5zThBh0QFog,2677
18
+ phylogenie/treesimulator/__init__.py,sha256=XG_xwETKWgDmCihqNUFCcMHtFg4WvZu5qbqWn9Dndt8,879
19
+ phylogenie/treesimulator/events/__init__.py,sha256=UGfvXOVJ_ZAkk_8sBPihjmxciiaEnXZEPFIY53sttWI,940
20
+ phylogenie/treesimulator/events/contact_tracing.py,sha256=_nJ85yhgGkeruQgMHvGpDYoyhheBf8M4LgZWiWdi5dY,4801
21
+ phylogenie/treesimulator/events/core.py,sha256=JokGmieAv2xEX7KsjBWZr05jHN1jB-XZbpxe9gwdbDA,7953
22
+ phylogenie/treesimulator/events/mutations.py,sha256=xkXUIppbLIWZqKwVf-hi7d-_pS42TG2EPVfJA_grxBg,3443
23
+ phylogenie/treesimulator/gillespie.py,sha256=TMDNKBkFwVyAEhBlbwxCTA61GuGwP-42HxpsAVXiU0s,4275
24
+ phylogenie/treesimulator/model.py,sha256=0Im6cFTlpMlJrSP4pTTKtvLT9qrQWV8MSTesAsBxT8g,5422
25
+ phylogenie/typeguards.py,sha256=JtqmbEWJZBRHbWgCvcl6nrWm3VcBfzRbklbTBYHItn0,1325
26
+ phylogenie/typings.py,sha256=GknvAFXyiaWeeYJ8Lk5d6E2VHT-xW6ONEojYbtJYiB8,476
27
+ phylogenie/utils.py,sha256=pCg9ob0RpLUHwM49x4knKxL4FNPr3-EU_6zMXsvxtAg,370
28
+ phylogenie-2.1.2.dist-info/LICENSE.txt,sha256=NUrDqElK-eD3I0WqC004CJsy6cs0JgsAoebDv_42-pw,1071
29
+ phylogenie-2.1.2.dist-info/METADATA,sha256=RoxXfMaPI6auRdjONAE8HfcfhZjHX0hTmx_oJlrzaws,5375
30
+ phylogenie-2.1.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
31
+ phylogenie-2.1.2.dist-info/entry_points.txt,sha256=Rt6_usN0FkBX1ZfiqCirjMN9FKOgFLG8rydcQ8kugeE,51
32
+ phylogenie-2.1.2.dist-info/RECORD,,
@@ -1,28 +0,0 @@
1
- phylogenie/__init__.py,sha256=sv8sfqkbarYgeaCFGiGMtV_fTUPuXEyJS_I3W9mlIto,1801
2
- phylogenie/generators/__init__.py,sha256=zsOxy28-9j9alOQLIgrOAFfmM58NNHO_NEtW-KXQXAY,888
3
- phylogenie/generators/alisim.py,sha256=dDqlSwLDbRE2u5SZlsq1mArobTBtuk0aeXY3m1N-bWA,2374
4
- phylogenie/generators/configs.py,sha256=4jSBUZiFo2GacXWed5dy7lUEkaOWZkZG-KY9vHfhqGU,993
5
- phylogenie/generators/dataset.py,sha256=UTsf8u868_8K6aMwIpLZrIfSY7s9skXlLUqQBuetiNQ,2954
6
- phylogenie/generators/factories.py,sha256=Y7cTsIblyV9T7ZhYvyQ5Wd7JcCpMG1dkEF8jJ1iQxN8,6928
7
- phylogenie/generators/trees.py,sha256=R5ZlZ-UNy2euPBt2P3lyit4txC3xweLrPPA1FI6m5PQ,9210
8
- phylogenie/generators/typeguards.py,sha256=yj4VkhOaUXJ2OrY-6zhOeY9C4yKIQxjZtk2d-vIxttQ,828
9
- phylogenie/io.py,sha256=d1xF6rwER6KpnA5OrFgDn7Ow-YymvxA7OXX7hi_nfB4,2951
10
- phylogenie/main.py,sha256=vtvSpQxBNlYABoFQ25czl-l3fIr4QRo3svWVd-jcArw,1170
11
- phylogenie/msa.py,sha256=JDGyZUsAq6-m-SQjoCDjAkAZIxfgyl_PDIhdYn5HOow,2064
12
- phylogenie/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- phylogenie/skyline/__init__.py,sha256=7pF4CUb4ZCLzNYJNhOjpuTOLTRhlK7L6ugfccNqjIGo,620
14
- phylogenie/skyline/matrix.py,sha256=Gl8OgKjtieG0NwPYiPimKI36gefV8fm_OeorjdXxPTs,9146
15
- phylogenie/skyline/parameter.py,sha256=EM9qlPt0JhMBy3TbztM0dj24BaGNEy8KWKdTObDKhbI,4644
16
- phylogenie/skyline/vector.py,sha256=bJP7_FNX_Klt6wXqsyfj0KX3VNj6-dIhzCKSJuQcOV0,7115
17
- phylogenie/tree.py,sha256=dk8Sj1tqyGOunVO2crtIqb0LH-ws-PXqA8SuNcYfVHI,1738
18
- phylogenie/treesimulator/__init__.py,sha256=DGn_sRDwL4OY1x1fT36kh4ghhwqSGt_8FnrV_TcQCjs,563
19
- phylogenie/treesimulator/events.py,sha256=xV64Y_oH9tsAFVVJEGW6-VgiOcX-xNPR_niTxTmpARo,10583
20
- phylogenie/treesimulator/gillespie.py,sha256=q5t0jfZWRqoyoiXiImMmo8fXqo7Cw1ea-OS_8aCD6Mc,4491
21
- phylogenie/treesimulator/model.py,sha256=Zl82nlbuq0htrLZV7x5LAB-thuN4lzbDv5pDSrG3oM8,4595
22
- phylogenie/typeguards.py,sha256=JtqmbEWJZBRHbWgCvcl6nrWm3VcBfzRbklbTBYHItn0,1325
23
- phylogenie/typings.py,sha256=O1X6lGKTjJ2YJz3ApQ-rYb_tEJNUIcHdUIeYlSM4s5o,500
24
- phylogenie-2.1.0.dist-info/LICENSE.txt,sha256=NUrDqElK-eD3I0WqC004CJsy6cs0JgsAoebDv_42-pw,1071
25
- phylogenie-2.1.0.dist-info/METADATA,sha256=5_QZd6c3rvlHwhRC-TT6bqcNG6gZDJJGLuNWVUorzlg,5472
26
- phylogenie-2.1.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
27
- phylogenie-2.1.0.dist-info/entry_points.txt,sha256=Rt6_usN0FkBX1ZfiqCirjMN9FKOgFLG8rydcQ8kugeE,51
28
- phylogenie-2.1.0.dist-info/RECORD,,