Skip to content

Commit

Permalink
update to follow spec
Browse files Browse the repository at this point in the history
  • Loading branch information
apragsdale committed Jan 21, 2021
1 parent 2bdd194 commit 4848345
Show file tree
Hide file tree
Showing 8 changed files with 255 additions and 156 deletions.
66 changes: 34 additions & 32 deletions demes/demes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1366,60 +1366,68 @@ def deme(
This must be provided.
:param final_size: The final population size of the deme. If ``None``,
the deme has a constant ``initial_size`` population size.
:param float selfing_rate: The default selfing rate for this deme.
May be ``None``.
:param float cloning_rate: The default cloning rate for this deme.
May be ``None``.
:param epochs: Epochs that define population sizes, selfing rates, and
cloning rates, for the deme over various time periods.
If not specified, a single epoch will be created for the deme that
spans from ``start_time`` to ``end_time``, using the ``initial_size``,
``final_size``, ``selfing_rate`` and ``cloning_rate`` provided.
:param defaults: Default attributes for epochs, including cloning_rate
and selfing_rate.
:return: Newly created deme.
:rtype: :class:`.Deme`
"""
# some basic deme property checks
if id in self:
raise ValueError(f"deme {id} already exists in this graph")
if epochs is None:
raise ValueError(f"deme {id} must have at least one specified epoch")
if initial_size is None and epochs is not None:
initial_size = epochs[0].initial_size
if initial_size is None:
raise ValueError(f"must set initial_size for {id}")
raise ValueError(f"must set initial_size for deme {id}")
if ancestors is None:
ancestors = []
if not isinstance(ancestors, list):
raise TypeError("ancestors must be a list of deme IDs")
raise TypeError(f"ancestors must be a list of deme IDs for deme {id}")
for ancestor in ancestors:
if ancestor not in self:
raise ValueError(f"ancestor deme {ancestor} not in graph")
raise ValueError(f"ancestor deme {ancestor} not in graph for deme {id}")
if proportions is None:
if len(ancestors) == 1:
proportions = [1.0]
else:
proportions = []
if epochs is None:
raise ValueError("Demes must have at least one specified epoch")

# set the start time to first epoch's start time, to inf or to
# the ancestor's end time, if not given
# set the start time to first epoch's start time.
# if first epoch does not have a start time, set to inf or to
# the ancestor's end time
if start_time is None:
if epochs is not None and epochs[0].start_time is not None:
if epochs[0].start_time is not None:
start_time = epochs[0].start_time
elif len(ancestors) > 0:
if len(ancestors) > 1:
raise ValueError(
"with multiple ancestors, start_time must be specified"
f"with multiple ancestors, start_time must be specified for deme {id}"
)
start_time = self[ancestors[0]].end_time
else:
start_time = float("inf")
# set the end time to the last epoch's end time or to zero if not given
# the first epoch's start time is set to deme's start time
if epochs[0].start_time is None:
epochs[0].start_time = start_time
elif epochs[0].start_time != start_time:
raise ValueError(
f"deme and first epoch start times do not align for deme {id}"
)

# set the end time to the last epoch's end time
if end_time is None:
if epochs[-1].end_time is not None:
end_time = epochs[-1].end_time
else:
end_time = 0
end_time = epochs[-1].end_time
if epochs[-1].end_time is not None and epochs[-1].end_time != end_time:
raise ValueError("deme and final epoch end times do not align")
raise ValueError(
f"deme and final epoch end times do not align for deme {id}"
)

# check start time is valid wrt ancestor time intervals
for ancestor in ancestors:
anc = self[ancestor]
Expand All @@ -1429,6 +1437,7 @@ def deme(
f"of existence for ancestor {ancestor} "
f"({anc.start_time}, {anc.end_time})"
)

# set default cloning and selfing rates
if "selfing_rate" in defaults:
selfing_rate = defaults["selfing_rate"]
Expand All @@ -1443,24 +1452,17 @@ def deme(
else:
cloning_rate = None

# the last epoch's end time is set to deme's end time
if epochs[-1].end_time is None:
epochs[-1].end_time = end_time
# the first epoch's start time is set to deme's start time
if epochs[0].start_time is None:
epochs[0].start_time = start_time
elif epochs[0].start_time != start_time:
raise ValueError("deme and first epoch start times do not align")

# fill in all attributes of epochs
for i in range(len(epochs)):
# set the start and end times based on surrounding demes
# set the start time based on the last epoch's end time
if epochs[i].start_time is None:
epochs[i].start_time = epochs[i - 1].end_time
if epochs[i].end_time is None:
if epochs[i + 1].start_time is None:
raise ValueError("ambiguity about epochs' start/end times")
epochs[i].end_time = epochs[i + 1].start_time
raise ValueError("all epochs must specify the end time")
if i > 0 and epochs[i].start_time != epochs[i - 1].end_time:
raise ValueError(
"epoch start and end times do not align for deme {id}, epochs {i - 1} and {i}"
)
# for each subsequent epoch, fill in start size, final size,
# and size function as necessary based on last epoch
if epochs[i].initial_size is None:
Expand Down
4 changes: 4 additions & 0 deletions examples/browning_america.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,21 @@ demes:
ancestors: [AMH]
epochs:
- initial_size: 14474
end_time: 0
- id: EUR
description: European population
ancestors: [OOA]
epochs:
- initial_size: 1000
final_size: 34039
end_time: 0
- id: EAS
description: East Asian population
ancestors: [OOA]
epochs:
- initial_size: 510
final_size: 45852
end_time: 0
- id: ADMIX
description: Admixed America
ancestors: [AFR, EUR, EAS]
Expand All @@ -46,6 +49,7 @@ demes:
- start_time: 12
initial_size: 30000
final_size: 54664
end_time: 0
migrations:
symmetric:
- demes: [AFR, OOA]
Expand Down
3 changes: 3 additions & 0 deletions examples/gutenkunst_ooa.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,21 @@ demes:
ancestors: [AMH]
epochs:
- initial_size: 12300
end_time: 0
- id: CEU
description: Utah Residents (CEPH) with Northern and Western European Ancestry
ancestors: [OOA]
epochs:
- initial_size: 1000
final_size: 29725
end_time: 0
- id: CHB
description: Han Chinese in Beijing, China
ancestors: [OOA]
epochs:
- initial_size: 510
final_size: 54090
end_time: 0
migrations:
symmetric:
- demes: [YRI, OOA]
Expand Down
4 changes: 4 additions & 0 deletions examples/jacobs_papuans.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,19 @@ demes:
epochs:
- start_time: 12500.0
initial_size: 13249.0
end_time: 0
- id: Den1
ancestors: [DenA]
epochs:
- start_time: 9750.0
initial_size: 13249.0
end_time: 0
- id: Nea1
ancestors: [NeaA]
epochs:
- start_time: 3375.0
initial_size: 13249.0
end_time: 0
- id: Ghost
ancestors: [YRI]
start_time: 2218.0
Expand Down Expand Up @@ -84,6 +87,7 @@ demes:
epochs:
- start_time: 1293.0
initial_size: 6962.0
end_time: 0
migrations:
symmetric:
- demes: [YRI, Ghost]
Expand Down
3 changes: 3 additions & 0 deletions examples/offshoots.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,21 @@ demes:
description: Main population
epochs:
- initial_size: 1000
end_time: 0
- id: offshoot1
description: More recent offshoot population
ancestors: [ancestral]
epochs:
- start_time: 500
initial_size: 100
end_time: 0
- id: offshoot2
description: More ancient offshoot population
ancestors: [ancestral]
epochs:
- start_time: 1000
initial_size: 200
end_time: 0
migrations:
asymmetric:
- source: ancestral
Expand Down
Loading

0 comments on commit 4848345

Please sign in to comment.