"""
Elementary Dependency Structures (EDS).
"""
from collections.abc import Iterable
from typing import Any
from delphin.lnk import Lnk
from delphin.sembase import ArgumentStructure, Predication, SemanticStructure
BOUND_VARIABLE_ROLE = "BV"
PREDICATE_MODIFIER_ROLE = "ARG1"
##############################################################################
##############################################################################
# EDS classes
[docs]
class Node(Predication[str]):
"""
An EDS node.
Args:
id: node identifier
predicate: semantic predicate
type: node type (corresponds to the intrinsic variable type in MRS)
edges: mapping of outgoing edge roles to target identifiers
properties: morphosemantic properties
carg: constant value (e.g., for named entities)
lnk: surface alignment
surface: surface string
base: base form
Attributes:
id: node identifier
predicate: semantic predicate
type: node type (corresponds to the intrinsic variable type in MRS)
edges: mapping of outgoing edge roles to target identifiers
properties: morphosemantic properties
carg: constant value (e.g., for named entities)
lnk: surface alignment
cfrom: surface alignment starting position
cto: surface alignment ending position
surface: surface string
base: base form
"""
__slots__ = ("carg", "edges", "properties")
edges: dict[str, str]
properties: dict[str, str]
carg: str | None
def __init__(
self,
id: str,
predicate: str,
type: str | None = None,
edges: dict[str, str] | None = None,
properties: dict[str, str] | None = None,
carg: str | None = None,
lnk: Lnk | None = None,
surface=None,
base=None,
) -> None:
if not edges:
edges = {}
if not properties:
properties = {}
super().__init__(id, predicate, type, lnk, surface, base)
self.edges = edges
self.properties = properties
self.carg = carg
def __eq__(self, other: Any) -> bool:
if not isinstance(other, Node):
return NotImplemented
return (
self.predicate == other.predicate
and self.type == other.type
and self.edges == other.edges
and self.properties == other.properties
and self.carg == other.carg
)
[docs]
class EDS(SemanticStructure[str, Node]):
"""
An Elementary Dependency Structure (EDS) instance.
EDS are semantic structures deriving from MRS, but they are not
interconvertible with MRS as the do not encode a notion of
quantifier scope.
Args:
top: the id of the graph's top node
nodes: an iterable of EDS nodes
lnk: surface alignment
surface: surface string
identifier: a discourse-utterance identifier
"""
__slots__ = ()
def __init__(
self,
top: str | None = None,
nodes: Iterable[Node] | None = None,
lnk: Lnk | None = None,
surface=None,
identifier=None,
) -> None:
if nodes is None:
nodes = []
super().__init__(top, list(nodes), lnk, surface, identifier)
def __eq__(self, other: Any) -> bool:
if not isinstance(other, EDS):
return NotImplemented
return self.top == other.top and self.nodes == other.nodes
@property
def nodes(self) -> list[Node]:
"""Alias of :attr:`predications`."""
return self.predications
@property
def edges(self) -> list[tuple[str, str, str]]:
"""The list of all edges."""
edges: list[tuple[str, str, str]] = []
for node in self.nodes:
edges.extend((node.id, role, target) for role, target in node.edges.items())
return edges
# SemanticStructure methods
[docs]
def arguments(
self,
types: Iterable[str] | None = None,
expressed: bool | None = None,
) -> ArgumentStructure[str]:
args: ArgumentStructure[str] = {}
if types is not None:
ntypes = {node.id: node.type for node in self.nodes}
else:
ntypes = {}
for node in self.nodes:
args[node.id] = []
for role, target in node.edges.items():
ntype = ntypes.get(target)
if types is None or (ntype is not None and ntype in types):
args[node.id].append((role, target))
return args
[docs]
def properties(self, id: str | None) -> dict[str, str]:
return self[id].properties
[docs]
def is_quantifier(self, id: str | None) -> bool:
"""
Return `True` if *id* is the id of a quantifier node.
"""
return BOUND_VARIABLE_ROLE in self[id].edges
[docs]
def quantification_pairs(
self,
) -> list[tuple[Node | None, Node | None]]:
qs: set[str] = set()
qmap: dict[str, Node] = {}
for src, roleargs in self.arguments().items():
for role, tgt in roleargs:
if role == BOUND_VARIABLE_ROLE:
qs.add(src)
qmap[tgt] = self[src]
pairs: list[tuple[Node | None, Node | None]] = []
# first pair non-quantifiers to their quantifier, if any
for node in self.nodes:
if node.id not in qs:
pairs.append((node, qmap.get(node.id)))
# for MRS any unpaired quantifiers are added here, but in EDS
# I'm not sure what an unpaired quantifier would look like
return pairs