Skip to content

Commit 24a7c2c

Browse files
dilpathBSnelling
authored andcommitted
move from sciml pr
1 parent 66035d9 commit 24a7c2c

2 files changed

Lines changed: 44 additions & 21 deletions

File tree

petab/v2/lint.py

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -446,31 +446,31 @@ def run(self, problem: Problem) -> ValidationIssue | None:
446446

447447
# check for uniqueness of all primary keys
448448
counter = Counter(c.id for c in problem.conditions)
449-
duplicates = {id_ for id_, count in counter.items() if count > 1}
449+
duplicates = sorted(id_ for id_, count in counter.items() if count > 1)
450450

451451
if duplicates:
452452
return ValidationError(
453453
f"Condition table contains duplicate IDs: {duplicates}"
454454
)
455455

456456
counter = Counter(o.id for o in problem.observables)
457-
duplicates = {id_ for id_, count in counter.items() if count > 1}
457+
duplicates = sorted(id_ for id_, count in counter.items() if count > 1)
458458

459459
if duplicates:
460460
return ValidationError(
461461
f"Observable table contains duplicate IDs: {duplicates}"
462462
)
463463

464464
counter = Counter(e.id for e in problem.experiments)
465-
duplicates = {id_ for id_, count in counter.items() if count > 1}
465+
duplicates = sorted(id_ for id_, count in counter.items() if count > 1)
466466

467467
if duplicates:
468468
return ValidationError(
469469
f"Experiment table contains duplicate IDs: {duplicates}"
470470
)
471471

472472
counter = Counter(p.id for p in problem.parameters)
473-
duplicates = {id_ for id_, count in counter.items() if count > 1}
473+
duplicates = sorted(id_ for id_, count in counter.items() if count > 1)
474474

475475
if duplicates:
476476
return ValidationError(
@@ -509,7 +509,11 @@ def run(self, problem: Problem) -> ValidationIssue | None:
509509
for experiment in problem.experiments:
510510
# Check that there are no duplicate timepoints
511511
counter = Counter(period.time for period in experiment.periods)
512-
duplicates = {time for time, count in counter.items() if count > 1}
512+
duplicates = sorted(
513+
time
514+
for time, count in counter.items()
515+
if count > 1
516+
)
513517
if duplicates:
514518
messages.append(
515519
f"Experiment {experiment.id} contains duplicate "
@@ -904,31 +908,47 @@ def run(self, problem: Problem) -> ValidationIssue | None:
904908

905909
# Mapping table is optional
906910
if problem.mappings:
907-
# Check that each id only occurs once
908-
counter = Counter(
909-
[
910-
getattr(mapping, attr)
911-
for mapping in problem.mappings
912-
for attr in ["petab_id", "model_id"]
913-
if getattr(mapping, attr)
914-
]
911+
# Check that each id, across both the petabEntityId and
912+
# modelEntityId columns, occurs only once
913+
must_be_unique_ids = []
914+
for mapping in problem.mappings:
915+
petab_id := getattr(mapping, "petab_id"):
916+
model_id := getattr(mapping, "model_id"):
917+
918+
if petab_id:
919+
must_be_unique_ids.append(petab_id)
920+
# Duplicates for annotation-only rows (identity mappings)
921+
# are permitted.
922+
if petab_id == model_id:
923+
continue
924+
if model_id:
925+
must_be_unique_ids.append(model_id)
926+
927+
non_unique_ids = sorted(
928+
id_
929+
for id_, count in Counter(must_be_unique_ids).items()
930+
if count > 1
915931
)
916-
non_unique = {id_ for id_, count in counter.items() if count > 1}
917-
if non_unique:
932+
if non_unique_ids:
918933
return ValidationError(
919-
f"Mapping table contains non-unique IDs: {non_unique}."
934+
f"Mapping table contains non-unique IDs: {non_unique_ids}."
920935
)
921936

922937
# petabEntityId is not defined elsewhere in the PEtab problem
923-
petab_ids_mapping = {m.petab_id for m in problem.mappings}
924-
defined_petab_ids = (
938+
new_petab_ids = {
939+
m.petab_id
940+
for m in problem.mappings
941+
# Ignore identity mappings used for annotation
942+
if m.petab_id != m.model_id
943+
}
944+
old_petab_ids = (
925945
{c.id for c in problem.conditions}
926946
| {e.id for e in problem.experiments}
927947
| {o.id for o in problem.observables}
928948
)
929-
if petab_ids_mapping & defined_petab_ids:
949+
if overdefined_ids := sorted(new_petab_ids & old_petab_ids):
930950
messages.append(
931-
f"PEtab IDs `{petab_ids_mapping & defined_petab_ids}` are "
951+
f"PEtab IDs `{overdefined_ids}` are "
932952
"defined in the mapping table but also defined through "
933953
"other PEtab tables."
934954
)
@@ -989,6 +1009,9 @@ def get_valid_parameters_for_parameter_table(
9891009
)
9901010

9911011
# Add petab ids from mapping table if they are used for aliasing
1012+
# FIXME only add mapping.petab_id to allowed parameter IDs list if it
1013+
# aliases an invalid PEtab ID? See
1014+
# https://github.com/PEtab-dev/libpetab-python/pull/482#discussion_r3420762034
9921015
for mapping in problem.mappings:
9931016
if mapping.model_id and mapping.model_id in parameter_ids.keys():
9941017
parameter_ids[mapping.petab_id] = None

tests/v2/test_lint.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def test_validate_initial_change_symbols():
112112
def test_check_mapping_table():
113113
"""Test checks related to the mapping table."""
114114
problem = Problem()
115-
# PySB model from PEtab test suite
115+
# FIXME see https://github.com/PEtab-dev/libpetab-python/pull/482#discussion_r3431330125
116116
problem.model = SbmlModel.from_antimony("a.mean = 1")
117117
problem.add_mapping(
118118
petab_id="a_m",

0 commit comments

Comments
 (0)