Repository: pybel/pybel

Issue:
Build metagraph from nested statements
Though a graph data structure doesn't directly enable edges to be treated as nodes, edges can carry additional information about transitivity. For example, `A -> (B -> C)` is added to the graph as `A -> B` and `B -> C`, but both edges could include information about inwards transitivity and outwards transitivity.

Each BELGraph could have an additional directed *metagraph* associated with it, where nodes are the hashes of edges in the original graph and edges denote the transitivity between them

Comments:

Patch:
diff --git a/src/pybel/parser/parse_bel.py b/src/pybel/parser/parse_bel.py
index 15af30bd..16f53149 100644
--- a/src/pybel/parser/parse_bel.py
+++ b/src/pybel/parser/parse_bel.py
@@ -262,6 +262,7 @@ class BELParser(BaseParser):
         :param required_annotations: Optional list of required annotations
         """
         self.graph = graph
+        self.metagraph = set()
 
         self.allow_nested = allow_nested
         self.disallow_unqualified_translocations = disallow_unqualified_translocations
@@ -637,17 +638,18 @@ class BELParser(BaseParser):
         if not self.allow_nested:
             raise NestedRelationWarning(self.get_line_number(), line, position)
 
-        self._handle_relation_harness(line, position, {
+        subject_hash = self._handle_relation_checked(line, position, {
             SUBJECT: tokens[SUBJECT],
             RELATION: tokens[RELATION],
             OBJECT: tokens[OBJECT][SUBJECT],
         })
 
-        self._handle_relation_harness(line, position, {
+        object_hash = self._handle_relation_checked(line, position, {
             SUBJECT: tokens[OBJECT][SUBJECT],
             RELATION: tokens[OBJECT][RELATION],
             OBJECT: tokens[OBJECT][OBJECT],
         })
+        self.metagraph.add((subject_hash, object_hash))
         return tokens
 
     def check_function_semantics(self, line: str, position: int, tokens: ParseResults) -> ParseResults:
@@ -784,6 +786,10 @@ class BELParser(BaseParser):
 
         Note: this can't be changed after instantiation!
         """
+        self._handle_relation_checked(line, position, tokens)
+        return tokens
+
+    def _handle_relation_checked(self, line, position, tokens):
         if not self.control_parser.citation:
             raise MissingCitationException(self.get_line_number(), line, position)
 
@@ -794,8 +800,7 @@ class BELParser(BaseParser):
         if missing_required_annotations:
             raise MissingAnnotationWarning(self.get_line_number(), line, position, missing_required_annotations)
 
-        self._handle_relation(tokens)
-        return tokens
+        return self._handle_relation(tokens)
 
     def handle_unqualified_relation(self, _, __, tokens: ParseResults) -> ParseResults:
         """Handle unqualified relations."""


Test patch:
diff --git a/tests/test_parse/test_parse_bel_relations.py b/tests/test_parse/test_parse_bel_relations.py
index 3246177f..03b2102d 100644
--- a/tests/test_parse/test_parse_bel_relations.py
+++ b/tests/test_parse/test_parse_bel_relations.py
@@ -575,6 +575,7 @@ class TestRelations(TestTokenParserBase):
 
         self.assert_has_edge(cat, h2o2)
         self.assert_has_edge(h2o2, apoptosis)
+        self.assertEqual(1, len(self.parser.metagraph))
 
         self.parser.lenient = False