Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add (invalid) test cases of proportions == 0.0 #167

Merged
merged 3 commits into from
Nov 19, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Disallow proportions == 0 in reference implementation
This also uses the terms 'proportion' and 'rate' to match the
json schema, rather than using the term 'fraction'.
grahamgower committed Nov 18, 2024
commit da7d32f7b79c66833817d80c7c493e5fc5fad69c
34 changes: 19 additions & 15 deletions reference_implementation/demes_parser.py
Original file line number Diff line number Diff line change
@@ -73,16 +73,16 @@ def parse(data: dict) -> Graph:
description=(str, None),
start_time=((str, numbers.Number), is_positive_or_json_infinity),
ancestors=(list, is_list_of_identifiers),
proportions=(list, is_list_of_fractions),
proportions=(list, is_list_of_proportions),
),
)

allowed_epoch_defaults = dict(
end_time=(numbers.Number, is_non_negative_and_finite),
start_size=(numbers.Number, is_positive_and_finite),
end_size=(numbers.Number, is_positive_and_finite),
selfing_rate=(numbers.Number, is_fraction),
cloning_rate=(numbers.Number, is_fraction),
selfing_rate=(numbers.Number, is_rate),
cloning_rate=(numbers.Number, is_rate),
size_function=(str, None),
)
check_defaults(global_epoch_defaults, allowed_epoch_defaults)
@@ -101,7 +101,7 @@ def parse(data: dict) -> Graph:
),
ancestors=pop_list(deme_data, "ancestors", [], str, is_identifier),
proportions=pop_list(
deme_data, "proportions", None, numbers.Number, is_fraction
deme_data, "proportions", None, numbers.Number, is_proportion
),
)

@@ -126,8 +126,8 @@ def parse(data: dict) -> Graph:
end_size=pop_number(
epoch_data, "end_size", None, is_positive_and_finite
),
selfing_rate=pop_number(epoch_data, "selfing_rate", 0, is_fraction),
cloning_rate=pop_number(epoch_data, "cloning_rate", 0, is_fraction),
selfing_rate=pop_number(epoch_data, "selfing_rate", 0, is_rate),
cloning_rate=pop_number(epoch_data, "cloning_rate", 0, is_rate),
size_function=pop_string(epoch_data, "size_function", None),
)
check_empty(epoch_data)
@@ -142,7 +142,7 @@ def parse(data: dict) -> Graph:
check_defaults(
migration_defaults,
dict(
rate=(numbers.Number, is_fraction),
rate=(numbers.Number, is_rate),
start_time=((numbers.Number, str), is_positive_or_json_infinity),
end_time=(numbers.Number, is_non_negative_and_finite),
source=(str, is_identifier),
@@ -153,7 +153,7 @@ def parse(data: dict) -> Graph:
for migration_data in pop_list(data, "migrations", []):
insert_defaults(migration_data, migration_defaults)
graph.add_migration(
rate=pop_number(migration_data, "rate", validator=is_fraction),
rate=pop_number(migration_data, "rate", validator=is_rate),
start_time=pop_number(
migration_data,
"start_time",
@@ -182,7 +182,7 @@ def parse(data: dict) -> Graph:
sources=(list, is_nonempty_list_of_identifiers),
dest=(str, is_identifier),
time=(numbers.Number, is_positive_and_finite),
proportions=(list, is_nonempty_list_of_fractions_with_sum_less_than_1),
proportions=(list, is_nonempty_list_of_proportions_with_sum_less_than_1),
),
)
for pulse_data in pop_list(data, "pulses", []):
@@ -202,7 +202,7 @@ def parse(data: dict) -> Graph:
"proportions",
default=[],
required_type=numbers.Number,
validator=is_fraction,
validator=is_proportion,
),
)
check_empty(pulse_data)
@@ -246,10 +246,14 @@ def is_non_negative_and_finite(value):
return value >= 0 and not math.isinf(value)


def is_fraction(value):
def is_rate(value):
return 0 <= value <= 1


def is_proportion(value):
return 0 < value <= 1


def is_nonempty(value):
return len(value) > 0

@@ -266,12 +270,12 @@ def is_nonempty_list_of_identifiers(value):
return is_list_of_identifiers(value) and len(value) > 0


def is_list_of_fractions(value):
return all(isinstance(v, numbers.Number) and is_fraction(v) for v in value)
def is_list_of_proportions(value):
return all(isinstance(v, numbers.Number) and is_proportion(v) for v in value)


def is_nonempty_list_of_fractions_with_sum_less_than_1(value):
return is_list_of_fractions(value) and len(value) > 0 and sum(value) <= 1
def is_nonempty_list_of_proportions_with_sum_less_than_1(value):
return is_list_of_proportions(value) and len(value) > 0 and sum(value) <= 1


def validate_item(name, value, required_type, validator=None):