Repository: pybel/pybel
Pull request #490 (2021-04-19 15:43:12)

Update interface for accessing transitivities
Blocked by #491

CC @steppi @johnbachman I went as far as keeping track of the transitivities before inside the parser, but they never made it into the final object. I'm not sure what the best way to extract these and propagate them in INDRA would be, but after this is merged, you could do the following (after installing https://github.com/cthoyt/selventa-knowledge/):

```python
import selventa_knowledge
import pybel.constants as pc

graph = graph = selventa_knowledge.get_graph()
key_to_edge = {
   k: (u, v, d)
   for u, v, k, d in graph.edges(keys=True, data=True)
}

for k1, k2 in graph.transitivities:
    u1, v1, d1 = key_to_edge[k1]
    u2, v2, d2 = key_to_edge[k2]
    evidence1 = d1.get(pc.EVIDENCE)
    evidence2 = d2.get(pc.EVIDENCE)
    ...
```



Patch:
diff --git a/src/pybel/parser/parse_bel.py b/src/pybel/parser/parse_bel.py
index 05c4e492..1ea1e855 100644
--- a/src/pybel/parser/parse_bel.py
+++ b/src/pybel/parser/parse_bel.py
@@ -275,7 +275,6 @@ class BELParser(BaseParser):
         :param required_annotations: Optional list of required annotations
         """
         self.graph = graph
-        self.metagraph = set()
 
         self.disallow_nested = disallow_nested
         self.disallow_unqualified_translocations = disallow_unqualified_translocations
@@ -705,7 +704,7 @@ class BELParser(BaseParser):
                 TARGET: tokens[TARGET][TARGET],
             },
         )
-        self.metagraph.add((subject_hash, object_hash))
+        self.graph.add_transitivity(subject_hash, object_hash)
         return tokens
 
     def check_function_semantics(self, line: str, position: int, tokens: ParseResults) -> ParseResults:
diff --git a/src/pybel/struct/graph.py b/src/pybel/struct/graph.py
index 26d23b46..b88fd11b 100644
--- a/src/pybel/struct/graph.py
+++ b/src/pybel/struct/graph.py
@@ -50,6 +50,11 @@ WarningTuple = Tuple[Optional[str], BELParserWarning, EdgeData]
 class BELGraph(nx.MultiDiGraph):
     """An extension to :class:`networkx.MultiDiGraph` to represent BEL."""
 
+    #: A set of pairs of hashes of edges over which there is transitivity.
+    #: For example, for the nested statement (P(X) -> P(Y)) -> P(Z) will have
+    #: a pair for (hash(P(X) -> P(Y)), hash(P(Y) -> P(Z)))
+    transitivities: Set[Tuple[str, str]]
+
     def __init__(
         self,
         name: Optional[str] = None,
@@ -117,6 +122,8 @@ class BELGraph(nx.MultiDiGraph):
         if path:
             self.path = path
 
+        self.transitivities = set()
+
         #: A reference to the parent graph
         self.parent = None
         self._count = CountDispatch(self)
@@ -390,6 +397,14 @@ class BELGraph(nx.MultiDiGraph):
     def __str__(self):
         return '{} v{}'.format(self.name, self.version)
 
+    def add_transitivity(self, k1: str, k2: str) -> None:
+        """Add a pair of edge hashes over which there is transitivity.
+
+        :param k1: The hash of the subject edge
+        :param k2: The hash of the object edge
+        """
+        self.transitivities.add((k1, k2))
+
     def add_warning(
         self,
         exception: BELParserWarning,


Test patch:
diff --git a/tests/test_parse/test_parse_bel_relations.py b/tests/test_parse/test_parse_bel_relations.py
index ca45f56e..8607028b 100644
--- a/tests/test_parse/test_parse_bel_relations.py
+++ b/tests/test_parse/test_parse_bel_relations.py
@@ -568,7 +568,7 @@ class TestRelations(TestTokenParserBase):
 
         self.assert_has_edge(cat, h2o2)
         self.assert_has_edge(h2o2, apoptosis)
-        self.assertEqual(1, len(self.parser.metagraph))
+        self.assertEqual(1, len(self.parser.graph.transitivities))
 
     def test_negativeCorrelation_withObjectVariant(self):
         """