Exemple #1
0
def format(tree: Tree,
           indent: Union[int, None] = -1,
           compact: bool = False) -> str:
    """
    Format *tree* into a PENMAN string.

    Args:
        tree: a Tree object
        indent: how to indent formatted strings
        compact: if ``True``, put initial attributes on the first line
    Returns:
        the PENMAN-serialized string of the Tree *t*
    Example:
        >>> import penman
        >>> print(penman.format(
        ...     ('b', [('/', 'bark-01'),
        ...            (':ARG0', ('d', [('/', 'dog')]))])))
        (b / bark-01
           :ARG0 (d / dog))
    """
    if not isinstance(tree, Tree):
        tree = Tree(tree)
    vars = [var for var, _ in tree.nodes()] if compact else []
    parts = ['# ::{}{}'.format(key, ' ' + value if value else value)
             for key, value in tree.metadata.items()]
    parts.append(_format_node(tree.node, indent, 0, set(vars)))
    return '\n'.join(parts)
Exemple #2
0
def test_issue_92():
    # https://github.com/goodmami/penman/issues/92
    g = codec.decode('(a / alpha :ARG0~e.0 (b / beta))')
    assert configure(g) == Tree(('a', [('/', 'alpha'),
                                       (':ARG0~e.0', ('b', [('/', 'beta')]))]))
    assert configure(g, top='b') == Tree(
        ('b', [('/', 'beta'), (':ARG0-of~e.0', ('a', [('/', 'alpha')]))]))
Exemple #3
0
 def format(self,
            tree: Tree,
            indent: Union[int, None] = -1,
            compact: bool = False) -> str:
     """
     Format *tree* into a PENMAN string.
     """
     if not isinstance(tree, Tree):
         tree = Tree(tree)
     vars = [var for var, _ in tree.nodes()] if compact else []
     parts = [
         '# ::{}{}'.format(key, ' ' + value if value else value)
         for key, value in tree.metadata.items()
     ]
     parts.append(self._format_node(tree.node, indent, 0, set(vars)))
     return '\n'.join(parts)
Exemple #4
0
def canonicalize_roles(t: Tree, model: Model) -> Tree:
    """
    Normalize roles in *t* so they are canonical according to *model*.

    This is a tree transformation instead of a graph transformation
    because the orientation of the pure graph's triples is not decided
    until the graph is configured into a tree.

    Args:
        t: a :class:`Tree` object
        model: a model defining role normalizations
    Returns:
        A new :class:`Tree` object with canonicalized roles.
    Example:
        >>> from penman.codec import PENMANCodec
        >>> from penman.models.amr import model
        >>> from penman.transform import canonicalize_roles
        >>> codec = PENMANCodec()
        >>> t = codec.parse('(c / chapter :domain-of 7)')
        >>> t = canonicalize_roles(t, model)
        >>> print(codec.format(t))
        (c / chapter
           :mod 7)
    """
    if model is None:
        model = Model()
    tree = Tree(_canonicalize_node(t.node, model), metadata=t.metadata)
    logger.info('Canonicalized roles: %s', tree)
    return tree
Exemple #5
0
def configure(g: Graph, top: Variable = None, model: Model = None) -> Tree:
    """
    Create a tree from a graph by making as few decisions as possible.

    A graph interpreted from a valid tree using :func:`interpret` will
    contain epigraphical markers that describe how the triples of a
    graph are to be expressed in a tree, and thus configuring this
    tree requires only a single pass through the list of triples. If
    the markers are missing or out of order, or if the graph has been
    modified, then the configuration process will have to make
    decisions about where to insert tree branches. These decisions are
    deterministic, but may result in a tree different than the one
    expected.

    Args:
        g: the :class:`~penman.graph.Graph` to configure
        top: the variable to use as the top of the graph; if ``None``,
            the top of *g* will be used
        model: the :class:`~penman.model.Model` used to configure the
            tree
    Returns:
        The configured :class:`~penman.tree.Tree`.
    Example:

        >>> from penman.graph import Graph
        >>> from penman import layout
        >>> g = Graph([('b', ':instance', 'bark-01'),
        ...            ('b', ':ARG0', 'd'),
        ...            ('d', ':instance', 'dog')])
        >>> t = layout.configure(g)
        >>> print(t)
        Tree(
          ('b', [
            ('/', 'bark-01'),
            (':ARG0', ('d', [
              ('/', 'dog')]))]))
    """
    if model is None:
        model = _default_model
    node, data, nodemap = _configure(g, top, model)
    # remove any superfluous POPs at the end (maybe from dereification)
    while data and data[-1] is POP:
        data.pop()
    # if any data remain, the graph was not properly annotated for a tree
    while data:
        skipped, var, data = _find_next(data, nodemap)
        data_count = len(data)
        if var is None or data_count == 0:
            raise LayoutError('possibly disconnected graph')
        _configure_node(var, data, nodemap, model)
        if len(data) >= data_count:
            raise LayoutError('possible cycle in configuration')
        data = skipped + data
        # remove any superfluous POPs
        while data and data[-1] is POP:
            data.pop()
    tree = Tree(node, metadata=g.metadata)
    logger.debug('Configured: %s', tree)
    return tree
Exemple #6
0
def test_issue_90():
    # https://github.com/goodmami/penman/issues/90

    g = Graph([('i', ':instance', 'iota'), ('i2', ':instance', 'i'),
               ('i', ':ARG0', 'i2')],
              top='i')
    assert reconfigure(g) == Tree(('i', [('/', 'iota'),
                                         (':ARG0', ('i2', [('/', 'i')]))]))
Exemple #7
0
def test_configure(amr_model):
    g = codec.decode('(a / A)')
    assert configure(g) == Tree(('a', [('/', 'A')]))
    with pytest.raises(LayoutError):
        configure(g, top='A')

    g = codec.decode('(a / A :consist-of (b / B))')
    assert configure(g) == Tree(('a', [('/', 'A'),
                                       (':consist-of', ('b', [('/', 'B')]))]))
    assert configure(g, top='b') == Tree(
        ('b', [('/', 'B'), (':consist', ('a', [('/', 'A')]))]))

    amr_codec = PENMANCodec(model=amr_model)
    g = amr_codec.decode('(a / A :consist-of (b / B))')
    assert configure(g, model=amr_model) == Tree(
        ('a', [('/', 'A'), (':consist-of', ('b', [('/', 'B')]))]))
    assert configure(g, top='b', model=amr_model) == Tree(
        ('b', [('/', 'B'), (':consist-of-of', ('a', [('/', 'A')]))]))
Exemple #8
0
def test_reconfigure():
    g = codec.decode('''
        (a / alpha
           :ARG0 b
           :ARG1 (g / gamma
                    :ARG0-of (b / beta)))''')
    # original order reconfiguration puts node definitions at first
    # appearance of a variable
    assert reconfigure(g) == Tree(
        ('a', [('/', 'alpha'), (':ARG0', ('b', [('/', 'beta')])),
               (':ARG1', ('g', [('/', 'gamma'), (':ARG0-of', 'b')]))]))
    # canonical order reconfiguration can also shift things like
    # inverted arguments
    assert reconfigure(g, key=model.canonical_order) == Tree(
        ('a', [('/', 'alpha'),
               (':ARG0', ('b', [('/', 'beta'),
                                (':ARG0', ('g', [('/', 'gamma')]))])),
               (':ARG1', 'g')]))
Exemple #9
0
def interpret(t: Tree, model: Model = None) -> Graph:
    """
    Interpret tree *t* as a graph using *model*.

    Tree interpretation is the process of transforming the nodes and
    edges of a tree into a directed graph. A semantic model determines
    which edges are inverted and how to deinvert them. If *model* is
    not provided, the default model will be used.

    Args:
        t: the :class:`~penman.tree.Tree` to interpret
        model: the :class:`~penman.model.Model` used to interpret *t*
    Returns:
        The interpreted :class:`~penman.graph.Graph`.
    Example:

        >>> from penman.tree import Tree
        >>> from penman import layout
        >>> t = Tree(
        ...   ('b', [
        ...     ('/', 'bark-01'),
        ...     ('ARG0', ('d', [
        ...       ('/', 'dog')]))]))
        >>> g = layout.interpret(t)
        >>> for triple in g.triples:
        ...     print(triple)
        ...
        ('b', ':instance', 'bark-01')
        ('b', ':ARG0', 'd')
        ('d', ':instance', 'dog')

    """
    if model is None:
        model = _default_model
    variables = {v for v, _ in t.nodes()}
    top, triples, epidata = _interpret_node(t.node, variables, model)
    epimap = {}
    for triple, epis in epidata:
        if triple in epimap:
            logger.warning(
                f'ignoring epigraph data for duplicate triple: {triple}'
            )
        else:
            epimap[triple] = epis
    g = Graph(triples, top=top, epidata=epimap, metadata=t.metadata)
    logger.info('Interpreted: %s', g)
    return g
Exemple #10
0
def rearrange(t: Tree,
              key: Callable[[Role], Any] = None,
              attributes_first: bool = False) -> None:
    """
    Sort the branches at each node in tree *t* according to *key*.

    Each node in a tree contains a list of branches. This function
    sorts those lists in-place using the *key* function, which accepts
    a role and returns some sortable criterion.

    If the *attributes_first* argument is ``True``, attribute branches
    are appear before any edges.

    Instance branches (``/``) always appear before any other branches.

    Example:
        >>> from penman import layout
        >>> from penman.model import Model
        >>> from penman.codec import PENMANCodec
        >>> c = PENMANCodec()
        >>> t = c.parse(
        ...   '(s / see-01'
        ...   '   :ARG1 (c / cat)'
        ...   '   :ARG0 (d / dog))')
        >>> layout.rearrange(t, key=Model().canonical_order)
        >>> print(c.format(t))
        (s / see-01
           :ARG0 (d / dog)
           :ARG1 (c / cat))
    """
    if attributes_first:
        variables = {node[0] for node in t.nodes()}
    else:
        variables = set()

    def sort_key(branch: Branch):
        role, target = branch
        if is_atomic(target):
            criterion1 = target in variables
        else:
            criterion1 = target[0] in variables
        criterion2 = True if key is None else key(role)
        return (criterion1, criterion2)

    _rearrange(t.node, sort_key)
Exemple #11
0
def test_issue_34():
    # https://github.com/goodmami/penman/issues/34
    g = codec.decode('''
        # ::snt I think you failed to not not act.
        (t / think
           :ARG0 (i / i)
           :ARG1 (f / fail
              :ARG0 (y / you)
              :ARG1 (a / act
                 :polarity -
                 :polarity -)))''')
    assert configure(g) == Tree(
        ('t', [('/', 'think'), (':ARG0', ('i', [('/', 'i')])),
               (':ARG1', ('f', [('/', 'fail'),
                                (':ARG0', ('y', [('/', 'you')])),
                                (':ARG1', ('a', [('/', 'act'),
                                                 (':polarity', '-'),
                                                 (':polarity', '-')]))]))]))
Exemple #12
0
 def _parse(self, tokens: TokenIterator) -> Tree:
     metadata = self._parse_comments(tokens)
     node = self._parse_node(tokens)
     tree = Tree(node, metadata=metadata)
     logger.debug('Parsed: %s', tree)
     return tree
Exemple #13
0
def test_issue_93():
    # https://github.com/goodmami/penman/issues/93
    g = codec.decode('(a / alpha :ARG0 b~1)')
    g.triples.append(('b', ':instance', 'beta'))
    assert configure(g) == Tree(('a', [('/', 'alpha'),
                                       (':ARG0', ('b', [('/', 'beta')]))]))