Beispiel #1
0
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
Beispiel #2
0
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
Beispiel #3
0
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
Beispiel #4
0
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