2017-01-12 03:06:36 +03:00
|
|
|
import pytest
|
2020-08-05 17:00:59 +03:00
|
|
|
from spacy.pipeline._parser_internals.nonproj import ancestors, contains_cycle
|
|
|
|
from spacy.pipeline._parser_internals.nonproj import is_nonproj_tree, is_nonproj_arc
|
2020-07-31 00:30:54 +03:00
|
|
|
from spacy.pipeline._parser_internals import nonproj
|
2018-07-25 00:38:44 +03:00
|
|
|
|
|
|
|
from ..util import get_doc
|
2016-02-24 13:26:25 +03:00
|
|
|
|
|
|
|
|
2017-01-12 03:06:36 +03:00
|
|
|
@pytest.fixture
|
|
|
|
def tree():
|
|
|
|
return [1, 2, 2, 4, 5, 2, 2]
|
2016-02-24 13:26:25 +03:00
|
|
|
|
2018-07-25 00:38:44 +03:00
|
|
|
|
2017-01-12 03:06:36 +03:00
|
|
|
@pytest.fixture
|
|
|
|
def cyclic_tree():
|
|
|
|
return [1, 2, 2, 4, 5, 3, 2]
|
2016-02-24 13:26:25 +03:00
|
|
|
|
2018-07-25 00:38:44 +03:00
|
|
|
|
2017-01-12 03:06:36 +03:00
|
|
|
@pytest.fixture
|
|
|
|
def partial_tree():
|
|
|
|
return [1, 2, 2, 4, 5, None, 7, 4, 2]
|
2016-02-24 13:26:25 +03:00
|
|
|
|
2018-07-25 00:38:44 +03:00
|
|
|
|
2017-01-12 03:06:36 +03:00
|
|
|
@pytest.fixture
|
|
|
|
def nonproj_tree():
|
|
|
|
return [1, 2, 2, 4, 5, 2, 7, 4, 2]
|
|
|
|
|
2018-07-25 00:38:44 +03:00
|
|
|
|
2017-01-12 03:06:36 +03:00
|
|
|
@pytest.fixture
|
|
|
|
def proj_tree():
|
|
|
|
return [1, 2, 2, 4, 5, 2, 7, 5, 2]
|
|
|
|
|
2018-07-25 00:38:44 +03:00
|
|
|
|
2017-01-12 03:06:36 +03:00
|
|
|
@pytest.fixture
|
|
|
|
def multirooted_tree():
|
|
|
|
return [3, 2, 0, 3, 3, 7, 7, 3, 7, 10, 7, 10, 11, 12, 18, 16, 18, 17, 12, 3]
|
|
|
|
|
|
|
|
|
|
|
|
def test_parser_ancestors(tree, cyclic_tree, partial_tree, multirooted_tree):
|
2018-11-27 03:09:36 +03:00
|
|
|
assert [a for a in ancestors(3, tree)] == [4, 5, 2]
|
|
|
|
assert [a for a in ancestors(3, cyclic_tree)] == [4, 5, 3, 4, 5, 3, 4]
|
|
|
|
assert [a for a in ancestors(3, partial_tree)] == [4, 5, None]
|
|
|
|
assert [a for a in ancestors(17, multirooted_tree)] == []
|
2017-01-12 03:06:36 +03:00
|
|
|
|
|
|
|
|
|
|
|
def test_parser_contains_cycle(tree, cyclic_tree, partial_tree, multirooted_tree):
|
2018-11-30 19:43:08 +03:00
|
|
|
assert contains_cycle(tree) is None
|
2020-07-07 19:46:00 +03:00
|
|
|
assert contains_cycle(cyclic_tree) == {3, 4, 5}
|
2018-11-30 19:43:08 +03:00
|
|
|
assert contains_cycle(partial_tree) is None
|
|
|
|
assert contains_cycle(multirooted_tree) is None
|
2017-01-12 03:06:36 +03:00
|
|
|
|
|
|
|
|
|
|
|
def test_parser_is_nonproj_arc(nonproj_tree, partial_tree, multirooted_tree):
|
2018-11-30 19:43:08 +03:00
|
|
|
assert is_nonproj_arc(0, nonproj_tree) is False
|
|
|
|
assert is_nonproj_arc(1, nonproj_tree) is False
|
|
|
|
assert is_nonproj_arc(2, nonproj_tree) is False
|
|
|
|
assert is_nonproj_arc(3, nonproj_tree) is False
|
|
|
|
assert is_nonproj_arc(4, nonproj_tree) is False
|
|
|
|
assert is_nonproj_arc(5, nonproj_tree) is False
|
|
|
|
assert is_nonproj_arc(6, nonproj_tree) is False
|
|
|
|
assert is_nonproj_arc(7, nonproj_tree) is True
|
|
|
|
assert is_nonproj_arc(8, nonproj_tree) is False
|
|
|
|
assert is_nonproj_arc(7, partial_tree) is False
|
|
|
|
assert is_nonproj_arc(17, multirooted_tree) is False
|
|
|
|
assert is_nonproj_arc(16, multirooted_tree) is True
|
2018-11-27 03:09:36 +03:00
|
|
|
|
|
|
|
|
|
|
|
def test_parser_is_nonproj_tree(
|
|
|
|
proj_tree, nonproj_tree, partial_tree, multirooted_tree
|
|
|
|
):
|
2018-11-30 19:43:08 +03:00
|
|
|
assert is_nonproj_tree(proj_tree) is False
|
|
|
|
assert is_nonproj_tree(nonproj_tree) is True
|
|
|
|
assert is_nonproj_tree(partial_tree) is False
|
|
|
|
assert is_nonproj_tree(multirooted_tree) is True
|
2017-01-12 03:06:36 +03:00
|
|
|
|
|
|
|
|
|
|
|
def test_parser_pseudoprojectivity(en_tokenizer):
|
|
|
|
def deprojectivize(proj_heads, deco_labels):
|
2018-11-27 03:09:36 +03:00
|
|
|
tokens = en_tokenizer("whatever " * len(proj_heads))
|
|
|
|
rel_proj_heads = [head - i for i, head in enumerate(proj_heads)]
|
|
|
|
doc = get_doc(
|
|
|
|
tokens.vocab,
|
|
|
|
words=[t.text for t in tokens],
|
|
|
|
deps=deco_labels,
|
|
|
|
heads=rel_proj_heads,
|
|
|
|
)
|
2017-05-22 13:39:03 +03:00
|
|
|
nonproj.deprojectivize(doc)
|
2017-01-12 03:06:36 +03:00
|
|
|
return [t.head.i for t in doc], [token.dep_ for token in doc]
|
|
|
|
|
2018-11-27 03:09:36 +03:00
|
|
|
# fmt: off
|
2017-01-12 03:06:36 +03:00
|
|
|
tree = [1, 2, 2]
|
|
|
|
nonproj_tree = [1, 2, 2, 4, 5, 2, 7, 4, 2]
|
|
|
|
nonproj_tree2 = [9, 1, 3, 1, 5, 6, 9, 8, 6, 1, 6, 12, 13, 10, 1]
|
2018-11-27 03:09:36 +03:00
|
|
|
labels = ["det", "nsubj", "root", "det", "dobj", "aux", "nsubj", "acl", "punct"]
|
|
|
|
labels2 = ["advmod", "root", "det", "nsubj", "advmod", "det", "dobj", "det", "nmod", "aux", "nmod", "advmod", "det", "amod", "punct"]
|
|
|
|
# fmt: on
|
2017-01-12 03:06:36 +03:00
|
|
|
|
2018-11-27 03:09:36 +03:00
|
|
|
assert nonproj.decompose("X||Y") == ("X", "Y")
|
|
|
|
assert nonproj.decompose("X") == ("X", "")
|
2018-11-30 19:43:08 +03:00
|
|
|
assert nonproj.is_decorated("X||Y") is True
|
|
|
|
assert nonproj.is_decorated("X") is False
|
2017-01-12 03:06:36 +03:00
|
|
|
|
2017-05-22 13:39:03 +03:00
|
|
|
nonproj._lift(0, tree)
|
2018-11-27 03:09:36 +03:00
|
|
|
assert tree == [2, 2, 2]
|
2017-01-12 03:06:36 +03:00
|
|
|
|
2018-11-27 03:09:36 +03:00
|
|
|
assert nonproj._get_smallest_nonproj_arc(nonproj_tree) == 7
|
|
|
|
assert nonproj._get_smallest_nonproj_arc(nonproj_tree2) == 10
|
2017-01-12 03:06:36 +03:00
|
|
|
|
2018-11-27 03:09:36 +03:00
|
|
|
# fmt: off
|
2017-05-22 13:39:03 +03:00
|
|
|
proj_heads, deco_labels = nonproj.projectivize(nonproj_tree, labels)
|
2018-11-27 03:09:36 +03:00
|
|
|
assert proj_heads == [1, 2, 2, 4, 5, 2, 7, 5, 2]
|
|
|
|
assert deco_labels == ["det", "nsubj", "root", "det", "dobj", "aux",
|
|
|
|
"nsubj", "acl||dobj", "punct"]
|
2017-01-12 03:06:36 +03:00
|
|
|
|
|
|
|
deproj_heads, undeco_labels = deprojectivize(proj_heads, deco_labels)
|
2018-11-27 03:09:36 +03:00
|
|
|
assert deproj_heads == nonproj_tree
|
|
|
|
assert undeco_labels == labels
|
2017-01-12 03:06:36 +03:00
|
|
|
|
2017-05-22 13:39:03 +03:00
|
|
|
proj_heads, deco_labels = nonproj.projectivize(nonproj_tree2, labels2)
|
2018-11-27 03:09:36 +03:00
|
|
|
assert proj_heads == [1, 1, 3, 1, 5, 6, 9, 8, 6, 1, 9, 12, 13, 10, 1]
|
|
|
|
assert deco_labels == ["advmod||aux", "root", "det", "nsubj", "advmod",
|
|
|
|
"det", "dobj", "det", "nmod", "aux", "nmod||dobj",
|
|
|
|
"advmod", "det", "amod", "punct"]
|
2017-01-12 03:06:36 +03:00
|
|
|
|
|
|
|
deproj_heads, undeco_labels = deprojectivize(proj_heads, deco_labels)
|
2018-11-27 03:09:36 +03:00
|
|
|
assert deproj_heads == nonproj_tree2
|
|
|
|
assert undeco_labels == labels2
|
2017-01-12 03:06:36 +03:00
|
|
|
|
|
|
|
# if decoration is wrong such that there is no head with the desired label
|
|
|
|
# the structure is kept and the label is undecorated
|
|
|
|
proj_heads = [1, 2, 2, 4, 5, 2, 7, 5, 2]
|
2018-11-27 03:09:36 +03:00
|
|
|
deco_labels = ["det", "nsubj", "root", "det", "dobj", "aux", "nsubj",
|
|
|
|
"acl||iobj", "punct"]
|
2017-01-12 03:06:36 +03:00
|
|
|
|
|
|
|
deproj_heads, undeco_labels = deprojectivize(proj_heads, deco_labels)
|
2018-11-27 03:09:36 +03:00
|
|
|
assert deproj_heads == proj_heads
|
|
|
|
assert undeco_labels == ["det", "nsubj", "root", "det", "dobj", "aux",
|
|
|
|
"nsubj", "acl", "punct"]
|
2017-01-12 03:06:36 +03:00
|
|
|
|
|
|
|
# if there are two potential new heads, the first one is chosen even if
|
2018-11-27 03:09:36 +03:00
|
|
|
# it"s wrong
|
2017-01-12 03:06:36 +03:00
|
|
|
proj_heads = [1, 1, 3, 1, 5, 6, 9, 8, 6, 1, 9, 12, 13, 10, 1]
|
2018-11-27 03:09:36 +03:00
|
|
|
deco_labels = ["advmod||aux", "root", "det", "aux", "advmod", "det",
|
|
|
|
"dobj", "det", "nmod", "aux", "nmod||dobj", "advmod",
|
|
|
|
"det", "amod", "punct"]
|
2017-01-12 03:06:36 +03:00
|
|
|
|
|
|
|
deproj_heads, undeco_labels = deprojectivize(proj_heads, deco_labels)
|
2018-11-27 03:09:36 +03:00
|
|
|
assert deproj_heads == [3, 1, 3, 1, 5, 6, 9, 8, 6, 1, 6, 12, 13, 10, 1]
|
|
|
|
assert undeco_labels == ["advmod", "root", "det", "aux", "advmod", "det",
|
|
|
|
"dobj", "det", "nmod", "aux", "nmod", "advmod",
|
|
|
|
"det", "amod", "punct"]
|
|
|
|
# fmt: on
|