From 87afa3321eb8cb8c9769b0ddf162a7ce9d2797ad Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Tue, 3 Jun 2025 08:52:47 -0400 Subject: [PATCH 1/6] Use simpler graph for prioritization of mappings --- src/semra/api.py | 24 +++++++++++++++++------- src/semra/io/graph.py | 17 +++++++++++++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/semra/api.py b/src/semra/api.py index 5003e276..850b6b75 100644 --- a/src/semra/api.py +++ b/src/semra/api.py @@ -17,7 +17,7 @@ from ssslm import LiteralMapping from tqdm.auto import tqdm -from semra.io.graph import _from_digraph_edge, to_digraph +from semra.io.graph import to_digraph, to_simple_graph from semra.rules import EXACT_MATCH, FLIP, INVERSION_MAPPING, SubsetConfiguration from semra.struct import ( Evidence, @@ -529,24 +529,34 @@ def prioritize(mappings: list[Mapping], priority: list[str]) -> list[Mapping]: and the priority reference is the object (skip the self mapping) """ original_mappings = len(mappings) - mappings = [m for m in mappings if m.predicate == EXACT_MATCH] + mappings_by_subj_obj = { + (m.subject.curie, m.object.curie): m for m in mappings + if m.predicate == EXACT_MATCH + } + # Gather all the references by CURIE + references_by_curie = { + ref.curie: ref for m in mappings for ref in (m.subject, m.object) + } + exact_mappings = len(mappings) priority = _clean_priority_prefixes(priority) - graph = to_digraph(mappings).to_undirected() + graph = to_simple_graph(mappings) rv: list[Mapping] = [] for component in tqdm(nx.connected_components(graph), unit="component", unit_scale=True): - o = get_priority_reference(component, priority) + component_references = [ + references_by_curie[curie] for curie in component + ] + o = get_priority_reference(component_references, priority) if o is None: continue rv.extend( - mapping + mappings_by_subj_obj[(s.curie, o.curie)] # TODO should this work even if s-o edge not exists? # can also do "inference" here, but also might be # because of negative edge filtering - for s in component + for s in component_references if s != o and graph.has_edge(s, o) - for mapping in _from_digraph_edge(graph, s, o) ) # sort such that the mappings are ordered by object by priority order diff --git a/src/semra/io/graph.py b/src/semra/io/graph.py index 0362448a..57339f41 100644 --- a/src/semra/io/graph.py +++ b/src/semra/io/graph.py @@ -16,6 +16,7 @@ "from_digraph", "from_multidigraph", "to_digraph", + "to_simple_graph", "to_multidigraph", ] @@ -56,6 +57,22 @@ def to_digraph(mappings: t.Iterable[Mapping]) -> nx.DiGraph: return graph +def to_simple_graph(mappings: t.Iterable[Mapping]) -> nx.Graph: + """Return an undirected graph capturing only the structure of mappings. + + :param mappings: An iterable of mappings + + :returns: An undirected graph in which the nodes are simple string CURIEs + corresponding to References. The edges are undirected and represent + the relationships between subject and object CURIEs in mappings. + """ + graph = nx.Graph() + edges = {(mapping.subject.curie, mapping.object.curie) + for mapping in mappings} + graph.add_edges_from(edges) + return graph + + def from_digraph(graph: nx.DiGraph) -> list[Mapping]: """Extract mappings from a simple directed graph data model.""" return [mapping for s, o in graph.edges() for mapping in _from_digraph_edge(graph, s, o)] From 92217b91923d68ecb43c34b7d3607bc047f505cb Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Tue, 3 Jun 2025 13:09:41 -0400 Subject: [PATCH 2/6] Adjust styling --- src/semra/api.py | 11 +++-------- src/semra/io/graph.py | 5 ++--- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/semra/api.py b/src/semra/api.py index 850b6b75..73693c3e 100644 --- a/src/semra/api.py +++ b/src/semra/api.py @@ -530,13 +530,10 @@ def prioritize(mappings: list[Mapping], priority: list[str]) -> list[Mapping]: """ original_mappings = len(mappings) mappings_by_subj_obj = { - (m.subject.curie, m.object.curie): m for m in mappings - if m.predicate == EXACT_MATCH + (m.subject.curie, m.object.curie): m for m in mappings if m.predicate == EXACT_MATCH } # Gather all the references by CURIE - references_by_curie = { - ref.curie: ref for m in mappings for ref in (m.subject, m.object) - } + references_by_curie = {ref.curie: ref for m in mappings for ref in (m.subject, m.object)} exact_mappings = len(mappings) priority = _clean_priority_prefixes(priority) @@ -544,9 +541,7 @@ def prioritize(mappings: list[Mapping], priority: list[str]) -> list[Mapping]: graph = to_simple_graph(mappings) rv: list[Mapping] = [] for component in tqdm(nx.connected_components(graph), unit="component", unit_scale=True): - component_references = [ - references_by_curie[curie] for curie in component - ] + component_references = [references_by_curie[curie] for curie in component] o = get_priority_reference(component_references, priority) if o is None: continue diff --git a/src/semra/io/graph.py b/src/semra/io/graph.py index 57339f41..53d8c13f 100644 --- a/src/semra/io/graph.py +++ b/src/semra/io/graph.py @@ -16,8 +16,8 @@ "from_digraph", "from_multidigraph", "to_digraph", - "to_simple_graph", "to_multidigraph", + "to_simple_graph", ] #: The key inside the data dictionary for a SeMRA mapping graph @@ -67,8 +67,7 @@ def to_simple_graph(mappings: t.Iterable[Mapping]) -> nx.Graph: the relationships between subject and object CURIEs in mappings. """ graph = nx.Graph() - edges = {(mapping.subject.curie, mapping.object.curie) - for mapping in mappings} + edges = {(mapping.subject.curie, mapping.object.curie) for mapping in mappings} graph.add_edges_from(edges) return graph From 6140076d1c77eab77b03e0613e6748912dacc816 Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Sun, 29 Jun 2025 10:39:50 +0200 Subject: [PATCH 3/6] Renames --- src/semra/api.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/semra/api.py b/src/semra/api.py index 73693c3e..56996bbc 100644 --- a/src/semra/api.py +++ b/src/semra/api.py @@ -529,11 +529,15 @@ def prioritize(mappings: list[Mapping], priority: list[str]) -> list[Mapping]: and the priority reference is the object (skip the self mapping) """ original_mappings = len(mappings) - mappings_by_subj_obj = { - (m.subject.curie, m.object.curie): m for m in mappings if m.predicate == EXACT_MATCH + mappings_by_subj_obj: dict[tuple[str, str], Mapping] = { + (mapping.subject.curie, mapping.object.curie): mapping + for mapping in mappings + if mapping.predicate == EXACT_MATCH } # Gather all the references by CURIE - references_by_curie = {ref.curie: ref for m in mappings for ref in (m.subject, m.object)} + curie_to_reference: dict[str, Reference] = { + reference.curie: reference for mapping in mappings for reference in (mapping.subject, mapping.object) + } exact_mappings = len(mappings) priority = _clean_priority_prefixes(priority) @@ -541,12 +545,12 @@ def prioritize(mappings: list[Mapping], priority: list[str]) -> list[Mapping]: graph = to_simple_graph(mappings) rv: list[Mapping] = [] for component in tqdm(nx.connected_components(graph), unit="component", unit_scale=True): - component_references = [references_by_curie[curie] for curie in component] + component_references = [curie_to_reference[curie] for curie in component] o = get_priority_reference(component_references, priority) if o is None: continue rv.extend( - mappings_by_subj_obj[(s.curie, o.curie)] + mappings_by_subj_obj[s.curie, o.curie] # TODO should this work even if s-o edge not exists? # can also do "inference" here, but also might be # because of negative edge filtering From ec1e930f3043d288258fd5cb1749cead20569ca2 Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Sun, 29 Jun 2025 10:41:11 +0200 Subject: [PATCH 4/6] Update api.py --- src/semra/api.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/semra/api.py b/src/semra/api.py index 51ff66e3..26d4dc2b 100644 --- a/src/semra/api.py +++ b/src/semra/api.py @@ -594,14 +594,6 @@ def prioritize( "in both directions between all nodes)" ) rv.append(mappings_by_subj_obj[s.curie, o.curie]) - rv.extend( - mappings_by_subj_obj[s.curie, o.curie] - # TODO should this work even if s-o edge not exists? - # can also do "inference" here, but also might be - # because of negative edge filtering - for s in component_references - if s != o and graph.has_edge(s, o) - ) # sort such that the mappings are ordered by object by priority order # then identifier of object, then subject prefix in alphabetical order From f90885dec04151f67752266c20e7712779695c4b Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Sun, 29 Jun 2025 10:47:21 +0200 Subject: [PATCH 5/6] Update api.py --- src/semra/api.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/semra/api.py b/src/semra/api.py index 26d4dc2b..49af7120 100644 --- a/src/semra/api.py +++ b/src/semra/api.py @@ -581,7 +581,8 @@ def prioritize( o = get_priority_reference(component_references, priority) if o is None: continue - for s in component: + for s_curie in component: + s = curie_to_reference[s_curie] if s == o: # don't add self-edges continue if not graph.has_edge(s, o): @@ -593,7 +594,7 @@ def prioritize( "that in a given component, it is a full clique (i.e., there are edges " "in both directions between all nodes)" ) - rv.append(mappings_by_subj_obj[s.curie, o.curie]) + rv.append(mappings_by_subj_obj[s_curie, o.curie]) # sort such that the mappings are ordered by object by priority order # then identifier of object, then subject prefix in alphabetical order From cf62ec561ed64ed0b28a103f4dd1de41c9372818 Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Sun, 29 Jun 2025 10:51:52 +0200 Subject: [PATCH 6/6] Update api.py --- src/semra/api.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/semra/api.py b/src/semra/api.py index 49af7120..97fbe26a 100644 --- a/src/semra/api.py +++ b/src/semra/api.py @@ -566,7 +566,9 @@ def prioritize( } # Gather all the references by CURIE curie_to_reference: dict[str, Reference] = { - reference.curie: reference for mapping in mappings for reference in (mapping.subject, mapping.object) + reference.curie: reference + for mapping in mappings + for reference in (mapping.subject, mapping.object) } exact_mappings = len(mappings) @@ -581,8 +583,7 @@ def prioritize( o = get_priority_reference(component_references, priority) if o is None: continue - for s_curie in component: - s = curie_to_reference[s_curie] + for s in component_references: if s == o: # don't add self-edges continue if not graph.has_edge(s, o): @@ -594,7 +595,7 @@ def prioritize( "that in a given component, it is a full clique (i.e., there are edges " "in both directions between all nodes)" ) - rv.append(mappings_by_subj_obj[s_curie, o.curie]) + rv.append(mappings_by_subj_obj[s.curie, o.curie]) # sort such that the mappings are ordered by object by priority order # then identifier of object, then subject prefix in alphabetical order