def reify_edges(g: Graph, model: Model) -> Graph: """ Reify all edges in *g* that have reifications in *model*. Args: g: a :class:`~penman.graph.Graph` object model: a model defining reifications Returns: A new :class:`~penman.graph.Graph` object with reified edges. Example: >>> from penman.codec import PENMANCodec >>> from penman.models.amr import model >>> from penman.transform import reify_edges >>> codec = PENMANCodec(model=model) >>> g = codec.decode('(c / chapter :mod 7)') >>> g = reify_edges(g, model) >>> print(codec.encode(g)) (c / chapter :ARG1-of (_ / have-mod-91 :ARG2 7)) """ vars = g.variables() if model is None: model = Model() new_epidata = dict(g.epidata) new_triples: List[BasicTriple] = [] for triple in g.triples: if model.is_role_reifiable(triple[1]): in_triple, node_triple, out_triple = model.reify(triple, vars) if appears_inverted(g, triple): in_triple, out_triple = out_triple, in_triple new_triples.extend((in_triple, node_triple, out_triple)) var = node_triple[0] vars.add(var) # manage epigraphical markers new_epidata[in_triple] = [Push(var)] old_epis = new_epidata.pop(triple) if triple in new_epidata else [] node_epis, out_epis = _edge_markers(old_epis) new_epidata[node_triple] = node_epis new_epidata[out_triple] = out_epis # we don't know where to put the final POP without configuring # the tree; maybe this should be a tree operation? else: new_triples.append(triple) g = Graph(new_triples, epidata=new_epidata, metadata=g.metadata) logger.info('Reified edges: %s', g) return g
def node_contexts(g: Graph) -> List[Union[Variable, None]]: """ Return the list of node contexts corresponding to triples in *g*. If a node context is unknown, the value ``None`` is substituted. Example: >>> from penman import decode, layout >>> g = decode(''' ... (a / alpha ... :attr val ... :ARG0 (b / beta :ARG0 (g / gamma)) ... :ARG0-of g)''') >>> for ctx, trp in zip(layout.node_contexts(g), trp): ... print(ctx, ':', trp) ... a : ('a', ':instance', 'alpha') a : ('a', ':attr', 'val') a : ('a', ':ARG0', 'b') b : ('b', ':instance', 'beta') b : ('b', ':ARG0', 'g') g : ('g', ':instance', 'gamma') a : ('g', ':ARG0', 'a') """ variables = g.variables() stack = [g.top] contexts: List[Union[Variable, None]] = [None] * len(g.triples) for i, triple in enumerate(g.triples): eligible: List[Variable] = [triple[0]] if triple[1] != CONCEPT_ROLE and triple[2] in variables: eligible.append(cast(Variable, triple[2])) if stack[-1] not in eligible: break else: contexts[i] = stack[-1] pushed = get_pushed_variable(g, triple) if pushed: stack.append(pushed) try: for epi in g.epidata[triple]: if epi is POP: stack.pop() except IndexError: break # more POPs than contexts in stack return contexts
def reify_attributes(g: Graph) -> Graph: """ Reify all attributes in *g*. Args: g: a :class:`~penman.graph.Graph` object Returns: A new :class:`~penman.graph.Graph` object with reified attributes. Example: >>> from penman.codec import PENMANCodec >>> from penman.models.amr import model >>> from penman.transform import reify_attributes >>> codec = PENMANCodec(model=model) >>> g = codec.decode('(c / chapter :mod 7)') >>> g = reify_attributes(g) >>> print(codec.encode(g)) (c / chapter :mod (_ / 7)) """ variables = g.variables() new_epidata = dict(g.epidata) new_triples: List[BasicTriple] = [] i = 2 for triple in g.triples: source, role, target = triple if role != CONCEPT_ROLE and target not in variables: # get unique var for new node var = '_' while var in variables: var = f'_{i}' i += 1 variables.add(var) role_triple = (source, role, var) node_triple = (var, CONCEPT_ROLE, target) new_triples.extend((role_triple, node_triple)) # manage epigraphical markers old_epis = new_epidata.pop(triple) if triple in new_epidata else [] role_epis, node_epis = _attr_markers(old_epis) new_epidata[role_triple] = role_epis + [Push(var)] new_epidata[node_triple] = node_epis + [POP] else: new_triples.append(triple) g = Graph(new_triples, epidata=new_epidata, metadata=g.metadata) logger.info('Reified attributes: %s', g) return g
def appears_inverted(g: Graph, triple: BasicTriple) -> bool: """ Return ``True`` if *triple* appears inverted in serialization. More specifically, this function returns ``True`` if *triple* has a :class:`Push` epigraphical marker in graph *g* whose associated variable is the source variable of *triple*. This should be accurate when testing a triple in a graph interpreted using :func:`interpret` (including :meth:`PENMANCodec.decode <penman.codec.PENMANCodec.decode>`, etc.), but it does not guarantee that a new serialization of *g* will express *triple* as inverted as it can change if the graph or its epigraphical markers are modified, if a new top is chosen, etc. Args: g: a :class:`~penman.graph.Graph` containing *triple* triple: the triple that does or does not appear inverted Returns: ``True`` if *triple* appears inverted in graph *g*. """ variables = g.variables() if triple[1] == CONCEPT_ROLE or triple[2] not in variables: # attributes and instance triples should never be inverted return False else: # edges may appear inverted... variable = get_pushed_variable(g, triple) if variable is not None: # ... when their source is pushed return variable == triple[0] else: # ... or when their target is the current node context for variable, _triple in zip(node_contexts(g), g.triples): if variable is None: break # we can no longer guess the node context elif _triple == triple: return triple[2] == variable return False