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

Adding option for non-redundant entialed relationships. #740

Merged
merged 5 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
792 changes: 481 additions & 311 deletions notebooks/Commands/Relationships.ipynb

Large diffs are not rendered by default.

2,164 changes: 1,919 additions & 245 deletions notebooks/Commands/ValidateDefinitions.ipynb

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
244 changes: 130 additions & 114 deletions src/oaklib/cli.py

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions src/oaklib/interfaces/obograph_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,38 @@ def descendant_graph(
self.transitive_query_cache[key] = g
return g

def non_redundant_entailed_relationships(
self,
predicates: List[PRED_CURIE] = None,
**kwargs,
) -> Iterator[RELATIONSHIP]:
"""
Yields all relationships that are directly entailed.

See https://github.com/INCATools/ontology-access-kit/issues/739

:param kwargs: same as relationships
:return:
"""
if "include_entailed" in kwargs:
kwargs.pop("include_entailed")
relationships = list(
self.relationships(predicates=predicates, include_entailed=True, **kwargs)
)
rel_by_sp = defaultdict(list)
for s, p, o in relationships:
if s == o:
continue
rel_by_sp[(s, p)].append(o)
for (s, p), objs in rel_by_sp.items():
redundant_set = set()
for o in objs:
ancs = list(self.ancestors(o, predicates=predicates, reflexive=False))
redundant_set.update(ancs)
for o in objs:
if o not in redundant_set:
yield s, p, o

def ancestors(
self,
start_curies: Union[CURIE, List[CURIE]],
Expand Down
2 changes: 2 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ def output_path(fn: str) -> str:
NUCLEUS = "GO:0005634"
ORGANELLE_MEMBRANE = "GO:0031090"
NUCLEAR_ENVELOPE = "GO:0005635"
ORGANELLE_ENVELOPE = "GO:0031967"
ENVELOPE = "GO:0031975"
THYLAKOID = "GO:0009579"
ATOM = "CHEBI:33250"
INTERNEURON = "CL:0000099"
Expand Down
39 changes: 39 additions & 0 deletions tests/test_implementations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
CYTOPLASM,
CYTOPLASMIC_REGION,
ENDOMEMBRANE_SYSTEM,
ENVELOPE,
EUKARYOTA,
FAKE_ID,
FUNGI,
Expand All @@ -120,6 +121,8 @@
NUCLEUS,
OPISTHOKONTA,
ORGANELLE,
ORGANELLE_ENVELOPE,
ORGANELLE_MEMBRANE,
PHENOTYPIC_ABNORMALITY,
PHOTORECEPTOR_OUTER_SEGMENT,
PHOTOSYNTHETIC_MEMBRANE,
Expand Down Expand Up @@ -615,6 +618,42 @@ def test_relationships(self, oi: BasicOntologyInterface, ignore_annotation_edges
irels = list(oi.incoming_relationships(o, predicates=[p]))
test.assertIn((p, s), irels)

def test_entailed_relationships(self, oi: OboGraphInterface):
"""
Tests entailed relationship methods for compliance.

:param oi:
:return:
"""
test = self.test
cases = [
(
NUCLEAR_MEMBRANE,
[IS_A, PART_OF],
{IS_A: {ORGANELLE_MEMBRANE}, PART_OF: {NUCLEAR_ENVELOPE}},
),
(
NUCLEAR_MEMBRANE,
[IS_A, OVERLAPS],
{IS_A: {ORGANELLE_MEMBRANE}, OVERLAPS: {NUCLEAR_ENVELOPE}},
),
(NUCLEAR_MEMBRANE, [IS_A], {IS_A: {ORGANELLE_MEMBRANE}}),
(
NUCLEAR_MEMBRANE,
[PART_OF],
{PART_OF: {NUCLEAR_ENVELOPE, ORGANELLE_ENVELOPE, ENVELOPE}},
),
]
for curie, preds, expected in cases:
logging.info(f"TESTS FOR {curie}")
rels = list(oi.non_redundant_entailed_relationships(subjects=[curie], predicates=preds))
objs_by_pred = {p: set() for p in preds}
for s, p, o in rels:
objs_by_pred[p].add(o)
assert s == curie
for p in preds:
test.assertCountEqual(expected[p], objs_by_pred[p])

def test_rbox_relationships(self, oi: BasicOntologyInterface):
"""
Tests relationships between relationship types
Expand Down
4 changes: 4 additions & 0 deletions tests/test_implementations/test_simple_obo.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ def test_relationships_extra(self):
def test_relationships(self):
self.compliance_tester.test_relationships(self.oi)

@unittest.skip("Contents of go-nucleus file need to be aligned")
def test_entailed_relationships(self):
self.compliance_tester.test_entailed_relationships(self.oi)

def test_rbox_relationships(self):
self.compliance_tester.test_rbox_relationships(self.oi)

Expand Down
4 changes: 4 additions & 0 deletions tests/test_implementations/test_sqldb.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ def test_relationships(self):
oi = SqlImplementation(OntologyResource(slug=f"sqlite:///{str(DB)}"))
self.compliance_tester.test_relationships(oi, ignore_annotation_edges=False)

def test_entailed_relationships(self):
oi = SqlImplementation(OntologyResource(slug=f"sqlite:///{str(DB)}"))
self.compliance_tester.test_entailed_relationships(oi)

def test_relationships_chunking(self):
"""
Tests behavior for chunking relationship queries
Expand Down
Loading