Skip to content

Commit

Permalink
Fix #316: Make EDS TOP-inference a bit more robust
Browse files Browse the repository at this point in the history
Also issue warnings on broken HCONS and uninferrable TOPs. Currently
warnings are issued for broken HCONS from TOP or from non-quantifier
predicates. Quantifiers are handled specially and their RSTR edges are
ignored, so they do not issue warnings.
  • Loading branch information
goodmami committed Dec 17, 2020
1 parent 7e66d6e commit 31ca209
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 25 deletions.
41 changes: 26 additions & 15 deletions delphin/eds/_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Operations on EDS
"""

import warnings
from itertools import count

from delphin import variable
Expand Down Expand Up @@ -39,10 +40,12 @@ def from_mrs(m, predicate_modifiers=False, unique_ids=True,
# EP id to node id map; create now to keep ids consistent
hcmap = {hc.hi: hc for hc in m.hcons}
reps = scope.representatives(m, priority=representative_priority)
ivmap = {e.iv : e for e in m.predications}
ivmap = {p.iv: (p, q)
for p, q in m.quantification_pairs()
if p is not None}

top = _mrs_get_top(m.top, hcmap, reps, m.index, ivmap)
deps = _mrs_args_to_basic_deps(m, hcmap, reps)
deps = _mrs_args_to_basic_deps(m, hcmap, ivmap, reps)
nodes = _mrs_to_nodes(m, deps)

e = eds.EDS(
Expand All @@ -66,25 +69,28 @@ def from_mrs(m, predicate_modifiers=False, unique_ids=True,


def _mrs_get_top(top, hcmap, reps, index, ivmap):
if top in hcmap:
if top in hcmap and hcmap[top].lo in reps:
lbl = hcmap[top].lo
if lbl in reps:
top = reps[lbl][0].id
else:
if top in hcmap:
warnings.warn(
f'broken handle constraint: {hcmap[top]}',
eds.EDSWarning
)
if top in reps:
top = reps[top][0].id
elif index in ivmap and ivmap[index][0].label in reps:
lbl = ivmap[index][0].label
top = reps[lbl][0].id
else:
top = reps[ivmap[index].label][0].id
elif top in reps:
top = reps[top][0].id
else:
top = None
warnings.warn('unable to find a suitable TOP', eds.EDSWarning)
top = None
return top


def _mrs_args_to_basic_deps(m, hcmap, reps):
ivmap = {p.iv: (p, q)
for p, q in m.quantification_pairs()
if p is not None}
def _mrs_args_to_basic_deps(m, hcmap, ivmap, reps):
edges = {}

for src, roleargs in m.arguments().items():
if src in ivmap:
p, q = ivmap[src]
Expand All @@ -97,6 +103,10 @@ def _mrs_args_to_basic_deps(m, hcmap, reps):
if lbl in reps:
tgt = reps[lbl][0].id
else:
warnings.warn(
f'broken handle constraint: {hcmap[tgt]}',
eds.EDSWarning
)
continue
# label arg
elif tgt in reps:
Expand Down Expand Up @@ -257,7 +267,8 @@ def make_ids_unique(e, m):
nids[nid] = next(new_ids)

# now use the unique ID mapping for reassignment
e.top = nids[e.top]
if e.top is not None:
e.top = nids[e.top]
for node in e.nodes:
node.id = nids[node.id]
edges = {role: nids[arg] for role, arg in node.edges.items()}
Expand Down
78 changes: 68 additions & 10 deletions tests/eds_test.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@

import pytest

from delphin.eds import EDS, Node
from delphin.eds import EDS, Node, from_mrs, EDSWarning
from delphin.mrs import MRS, EP, HCons


@pytest.fixture
def dogs_bark():
return {
'top': '_1',
'nodes': [Node('_1', '_bark_v_1', type='e', edges={'ARG1': '_3'}),
Node('_2', 'udef_q', edges={'BV': '_3'}),
Node('_3', '_dog_n_1', type='x')]
'top': 'e2',
'nodes': [Node('e2', '_bark_v_1', type='e', edges={'ARG1': 'x4'}),
Node('_1', 'udef_q', edges={'BV': 'x4'}),
Node('x4', '_dog_n_1', type='x')]
}


@pytest.fixture
def dogs_bark_mrs():
return MRS(
top='h0',
index='e2',
rels=[EP('_bark_v_1', label='h1', args={'ARG0': 'e2', 'ARG1': 'x4'}),
EP('udef_q', label='h3',
args={'ARG0': 'x4', 'RSTR': 'h5', 'BODY': 'h6'}),
EP('_dog_n_1', label='h7', args={'ARG0': 'x4'})],
hcons=[HCons.qeq('h0', 'h1'), HCons.qeq('h5', 'h7')]
)


def test_empty_EDS():
d = EDS()
assert d.top is None
Expand All @@ -22,17 +36,61 @@ def test_empty_EDS():

def test_basic_EDS(dogs_bark):
d = EDS(**dogs_bark)
assert d.top == '_1'
assert d.top == 'e2'
assert len(d.nodes) == 3

assert d.nodes[0].predicate == '_bark_v_1'
assert d.nodes[1].predicate == 'udef_q'
assert d.nodes[2].predicate == '_dog_n_1'

assert d.nodes[0].edges == {'ARG1': '_3'}
assert d.nodes[1].edges == {'BV': '_3'}
assert d.nodes[0].edges == {'ARG1': 'x4'}
assert d.nodes[1].edges == {'BV': 'x4'}
assert d.nodes[2].edges == {}

assert len(d.edges) == 2
assert d.edges[0] == ('_1', 'ARG1', '_3')
assert d.edges[1] == ('_2', 'BV', '_3')
assert d.edges[0] == ('e2', 'ARG1', 'x4')
assert d.edges[1] == ('_1', 'BV', 'x4')


def test_from_mrs(dogs_bark, dogs_bark_mrs):
d = from_mrs(dogs_bark_mrs)
e = EDS(**dogs_bark)
assert d[d.top] == e[e.top] and d.nodes == e.nodes
assert d == e

# recover TOP from INDEX
dogs_bark_mrs.top = None
d = from_mrs(dogs_bark_mrs)
e = EDS(**dogs_bark)
assert d == e

# no TOP or INDEX
dogs_bark_mrs.index = None
with pytest.warns(EDSWarning):
d = from_mrs(dogs_bark_mrs)
e = EDS(**{'top': None, 'nodes': dogs_bark['nodes']})
assert d == e

def test_from_mrs_broken_hcons_issue_319(dogs_bark_mrs):
# broken top
dogs_bark_mrs.rels[0].label = 'h99'
with pytest.warns(EDSWarning):
d = from_mrs(dogs_bark_mrs)
assert d.top == 'e2'

# it probably rained
m = MRS(
top='h0',
index='e2',
rels=[EP('_probable_a_1', label='h1', args={'ARG0': 'i4', 'ARG1': 'h5'}),
EP('_rain_v_1', label='h6', args={'ARG0': 'e2'})],
hcons=[HCons.qeq('h0', 'h1'), HCons.qeq('h5', 'h6')]
)
# no warning normally
e = from_mrs(m)
# broken hcons
m.rels[1].label = 'h99'
with pytest.warns(EDSWarning):
d = from_mrs(m)
assert len(d.nodes) == 2
assert len(d.arguments()['i4']) == 0

0 comments on commit 31ca209

Please sign in to comment.