Skip to main content

layout

Interpreting trees to graphs and configuring graphs to trees.

In order to serialize graphs into the PENMAN format, a tree-like layout of the graph must be decided. Deciding a layout includes choosing the order of the edges from a node and the paths to get to a node definition (the position in the tree where a node's concept and edges are specified). For instance, the following graphs for "The dog barked loudly" have different edge orders on the b node:

(b / bark-01           (b / bark-01
:ARG0 (d / dog) :mod (l / loud)
:mod (l / loud)) :ARG0 (d / dog))

With re-entrancies, there are choices about which location of a re-entrant node gets the full definition with its concept (node label), etc. For instance, the following graphs for "The dog tried to bark" have different locations for the definition of the d node:

(t / try-01              (t / try-01
:ARG0 (d / dog) :ARG0 d
:ARG1 (b / bark-01 :ARG1 (b / bark-01
:ARG0 d)) :ARG0 (d / dog))

With inverted edges, there are even more possibilities, such as:

(t / try-01                (t / try-01
:ARG0 (d / dog :ARG1 (b / bark-01
:ARG0-of b) :ARG0 (d / dog
:ARG1 (b / bark-01)) :ARG0-of t)))

This module introduces two epigraphical markers so that a pure graph parsed from PENMAN can retain information about its tree layout without altering its graph properties. The first marker type is Push, which is put on a triple to indicate that the triple introduces a new node context, while the sentinel POP indicates that a triple is at the end of one or more node contexts. These markers only work if the triples in the graph's data are ordered. For instance, one of the graphs above (repeated here) has the following data:

PENMAN                 Graph                            Epigraph
(t / try-01 [('t', ':instance', 'try-01'), :
:ARG0 (d / dog) ('t', ':ARG0', 'd'), : Push('d')
:ARG1 (b / bark-01 ('d', ':instance', 'dog'), : POP
:ARG0 d)) ('t', ':ARG1', 'b'), : Push('b')
('b', ':instance', 'bark-01'), :
('b', ':ARG0', 'd')] : POP

Function: interpret

interpret(t, options?): 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.

The options param consists of an object with a single model property.

Example

import { Tree, interpret } from 'penman-js';

const t = new Tree(['b', [
['/', 'bark-01'],
['ARG0', ['d', [
['/', 'dog']
]]]
]]);

const g = interpret(t);
for (const triple of g.triples) {
console.log(triple);
}
// ['b', ':instance', 'bark-01']
// ['b', ':ARG0', 'd']
// ['d', ':instance', 'dog']

Function: rearrange

rearrange(t, options?): void

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.

options consists of an object with optional key and attributesFirst properties.

  • If the attributesFirst argument is true, attribute branches will appear before any edges.
  • key is a function used for sorting branches.

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

Example

import { rearrange, Model, PENMANCodec } from 'penman-js';

const c = new PENMANCodec();
const t = c.parse(`
(s / see-01
:ARG1 (c / cat)
:ARG0 (d / dog))`);
rearrange(t, { key: Model().canonicalOrder, attributesFirst: true });
console.log(c.format(t));
// (s / see-01
// :ARG0 (d / dog)
// :ARG1 (c / cat))

Function: configure

configure(g, options?): Tree

Create a tree from a graph by making as few decisions as possible.

A graph interpreted from a valid tree using 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.

options consists of an object with optional top and model properties.

  • top is the variable to use as the top of the graph; if null, the top of g will be used.
  • model is the Model used to configure the tree.

Example

import { Graph, configure } from 'penman-js';

const g = new Graph([
['b', ':instance', 'bark-01'],
['b', ':ARG0', 'd'],
['d', ':instance', 'dog']
]);

const t = configure(g);
console.log(t);
// Tree('b', [['/', 'bark-01'], [':ARG0', ['d', [['/', 'dog']]]]])

Function: reconfigure

reconfigure(graph, options?): Tree

Create a tree from a graph after any discarding layout markers.

options consists of an object with optional top, model, and key properties. If key is provided, triples are sorted according to the key.


Function: getPushedVariable

getPushedVariable(g, triple): Variable | null

Return the variable pushed by triple, if any, otherwise null.

Example

import { decode, getPushedVariable } from 'penman-js';

const g = decode('(a / alpha :ARG0 (b / beta))');
console.log(getPushedVariable(g, ['a', ':instance', 'alpha'])); // Outputs: null
console.log(getPushedVariable(g, ['a', ':ARG0', 'b'])); // Outputs: 'b'

Function: appearsInverted

appearsInverted(g, triple): boolean

Return true if triple appears inverted in serialization.

More specifically, this function returns true if triple has a 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 interpret (including PENMANCodec.decode and similar methods), 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.


Function: nodeContexts

nodeContexts(g): (Variable | null)[]

Return the list of node contexts corresponding to triples in g.

If a node context is unknown, the value null is substituted.

Example

import { decode, nodeContexts } from 'penman-js';

const g = decode(`
(a / alpha
:attr val
:ARG0 (b / beta :ARG0 (g / gamma))
:ARG0-of g)`);
for (const [ctx, trp] of zip(nodeContexts(g), g.triples)) {
console.log(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']

Class: LayoutMarker

Epigraph marker for layout choices.

constructor

new LayoutMarker(): LayoutMarker


mode

mode: number = 0

The mode attribute specifies what the Epidatum annotates:

  • mode = 0 -- unspecified
  • mode = 1 -- role epidata
  • mode = 2 -- target epidata

Class: Pop

Epigraph marker to indicate the end of a node context.

constructor

new Pop(): Pop


mode

mode: number = 0

The mode attribute specifies what the Epidatum annotates:

  • mode = 0 -- unspecified
  • mode = 1 -- role epidata
  • mode = 2 -- target epidata

toString

toString(): string


Class: Push

Epigraph marker to indicate a new node context.

constructor

new Push(variable): Push


mode

mode: number = 0

The mode attribute specifies what the Epidatum annotates:

  • mode = 0 -- unspecified
  • mode = 1 -- role epidata
  • mode = 2 -- target epidata

variable

variable: string


toString

toString(): string


Variable: POP

Const POP: Pop

A singleton instance of Pop.