def _get_model(amr, model_file): if amr: from penman.models.amr import model elif model_file: model = Model(**json.load(model_file)) else: model = Model() return model
def _interpret_node(t: Node, variables: Set[Variable], model: Model): has_concept = False triples = [] epidata = {} var, edges = t for role, target in edges: epis: List[Epidatum] = [] if role == '/': role = CONCEPT_ROLE has_concept = True elif '~' in role: role, _, alignment = role.partition('~') epis.append(RoleAlignment.from_string(alignment)) # atomic targets if is_atomic(target): # remove any alignments if target and '~' in target: if target.startswith('"'): # need to handle alignments on strings differently # because strings may contain ~ inside the quotes pivot = target.rindex('"') + 1 if pivot < len(target): epis.append(Alignment.from_string(target[pivot:])) target = target[:pivot] else: target, _, alignment = target.partition('~') epis.append(Alignment.from_string(alignment)) triple = (var, role, target) if model.is_role_inverted(role): if target in variables: triple = model.invert(triple) else: logger.warning('cannot deinvert attribute: %r', triple) triples.append(triple) epidata[triple] = epis # nested nodes else: triple = model.deinvert((var, role, target[0])) triples.append(triple) epidata[triple] = epis # recurse to nested nodes epidata[triple].append(Push(target[0])) _, _triples, _epis = _interpret_node(target, variables, model) triples.extend(_triples) epidata.update(_epis) epidata[triples[-1]].append(POP) if not has_concept: instance = (var, CONCEPT_ROLE, None) triples.insert(0, instance) epidata[instance] = [] return var, triples, epidata
def test_is_concept_dereifiable(self, mini_amr): m = Model() assert not m.is_concept_dereifiable('chase-01') assert not m.is_concept_dereifiable(':mod') assert not m.is_concept_dereifiable('have-mod-91') m = Model.from_dict(mini_amr) assert not m.is_concept_dereifiable('chase-01') assert not m.is_concept_dereifiable(':mod') assert m.is_concept_dereifiable('have-mod-91')
def reify_edges(g: Graph, model: Model) -> Graph: """ Reify all edges in *g* that have reifications in *model*. Args: g: a :class:`~penman.graph.Graph` object model: a model defining reifications Returns: A new :class:`~penman.graph.Graph` object with reified edges. Example: >>> from penman.codec import PENMANCodec >>> from penman.models.amr import model >>> from penman.transform import reify_edges >>> codec = PENMANCodec(model=model) >>> g = codec.decode('(c / chapter :mod 7)') >>> g = reify_edges(g, model) >>> print(codec.encode(g)) (c / chapter :ARG1-of (_ / have-mod-91 :ARG2 7)) """ vars = g.variables() if model is None: model = Model() new_epidata = dict(g.epidata) new_triples: List[BasicTriple] = [] for triple in g.triples: if model.is_role_reifiable(triple[1]): in_triple, node_triple, out_triple = model.reify(triple, vars) if appears_inverted(g, triple): in_triple, out_triple = out_triple, in_triple new_triples.extend((in_triple, node_triple, out_triple)) var = node_triple[0] vars.add(var) # manage epigraphical markers new_epidata[in_triple] = [Push(var)] old_epis = new_epidata.pop(triple) if triple in new_epidata else [] node_epis, out_epis = _edge_markers(old_epis) new_epidata[node_triple] = node_epis new_epidata[out_triple] = out_epis # we don't know where to put the final POP without configuring # the tree; maybe this should be a tree operation? else: new_triples.append(triple) g = Graph(new_triples, epidata=new_epidata, metadata=g.metadata) logger.info('Reified edges: %s', g) return g
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
def _interpret_node(t: Node, variables: Set[Variable], model: Model): has_concept = False triples = [] epidata = {} var, edges = t for role, target in edges: epis: List[Epidatum] = [] if role == '/': role = CONCEPT_ROLE has_concept = True elif '~' in role: role, _, alignment = role.partition('~') epis.append(RoleAlignment.from_string(alignment)) # atomic targets if is_atomic(target): if target and '~' in target: target, _, alignment = target.partition('~') epis.append(Alignment.from_string(alignment)) triple = (var, role, target) if model.is_role_inverted(role): if target in variables: triple = model.invert(triple) else: logger.warning('cannot deinvert attribute: %r', triple) triples.append(triple) epidata[triple] = epis # nested nodes else: triple = model.deinvert((var, role, target[0])) triples.append(triple) epidata[triple] = epis # recurse to nested nodes epidata[triple].append(Push(target[0])) _, _triples, _epis = _interpret_node(target, variables, model) triples.extend(_triples) epidata.update(_epis) epidata[triples[-1]].append(POP) if not has_concept: instance = (var, CONCEPT_ROLE, None) triples.insert(0, instance) epidata[instance] = [] return var, triples, epidata
def _dereify_agenda(g: Graph, model: Model) -> _Dereification: alns = alignments(g) agenda: _Dereification = {} fixed: Set[Target] = set([g.top]) inst: Dict[Variable, BasicTriple] = {} other: Dict[Variable, List[BasicTriple]] = {} for triple in g.triples: var, role, tgt = triple if role == CONCEPT_ROLE: inst[var] = triple else: fixed.add(tgt) if var not in other: other[var] = [triple] else: other[var].append(triple) for var, instance in inst.items(): if (var not in fixed and len(other.get(var, [])) == 2 and model.is_concept_dereifiable(instance[2])): # passed initial checks # now figure out which other edge is the first one first, second = other[var] if get_pushed_variable(g, second) == var: first, second = second, first try: dereified = model.dereify(instance, first, second) except ModelError: pass else: # migrate epidata epidata: List[Epidatum] = [] if instance in alns: aln = alns[instance] epidata.append( RoleAlignment(aln.indices, prefix=aln.prefix)) epidata.extend(epi for epi in g.epidata[second] if not isinstance(epi, RoleAlignment)) agenda[var] = (first, dereified, epidata) return agenda
def _interpret_node(t: Node, variables: Set[Variable], model: Model): has_concept = False triples = [] epidata = [] var, edges = t for role, target in edges: epis: List[Epidatum] = [] role, role_epis = _process_role(role) epis.extend(role_epis) has_concept |= role == CONCEPT_ROLE # atomic targets if is_atomic(target): target, target_epis = _process_atomic(target) epis.extend(target_epis) triple = (var, role, target) if model.is_role_inverted(role): if target in variables: triple = model.invert(triple) else: logger.warning('cannot deinvert attribute: %r', triple) triples.append(triple) epidata.append((triple, epis)) # nested nodes else: triple = model.deinvert((var, role, target[0])) triples.append(triple) epis.append(Push(target[0])) epidata.append((triple, epis)) # recurse to nested nodes _, _triples, _epis = _interpret_node(target, variables, model) triples.extend(_triples) _epis[-1][1].append(POP) # POP from last triple of nested node epidata.extend(_epis) if not has_concept: instance = (var, CONCEPT_ROLE, None) triples.insert(0, instance) epidata.append((instance, [])) return var, triples, epidata
def _canonicalize_node(node: Node, model: Model) -> Node: var, edges = node canonical_edges = [] for i, edge in enumerate(edges): role, tgt = edge # alignments aren't parsed off yet, so handle them superficially role, tilde, alignment = role.partition('~') if not is_atomic(tgt): tgt = _canonicalize_node(tgt, model) canonical_role = model.canonicalize_role(role) + tilde + alignment canonical_edges.append((canonical_role, tgt)) return (var, canonical_edges)
def dereify_edges(g: Graph, model: Model) -> Graph: """ Dereify edges in *g* that have reifications in *model*. Args: g: a :class:`~penman.graph.Graph` object Returns: A new :class:`~penman.graph.Graph` object with dereified edges. Example: >>> from penman.codec import PENMANCodec >>> from penman.models.amr import model >>> from penman.transform import dereify_edges >>> codec = PENMANCodec(model=model) >>> g = codec.decode( ... '(c / chapter' ... ' :ARG1-of (_ / have-mod-91' ... ' :ARG2 7))') >>> g = dereify_edges(g, model) >>> print(codec.encode(g)) (c / chapter :mod 7) """ if model is None: model = Model() agenda = _dereify_agenda(g, model) new_epidata = dict(g.epidata) new_triples: List[BasicTriple] = [] for triple in g.triples: var = triple[0] if var in agenda: first, dereified, epidata = agenda[var] # only insert at the first triple so the dereification # appears in the correct location if triple == first: new_triples.append(dereified) new_epidata[dereified] = epidata if triple in new_epidata: del new_epidata[triple] else: new_triples.append(triple) g = Graph(new_triples, epidata=new_epidata, metadata=g.metadata) logger.info('Dereified edges: %s', g) return g
def load_amr_file(source, dereify=None, remove_wiki=False): assert remove_wiki in (False, 'replace', 'remove') # Select the model to use if dereify is None or dereify: # None or True (odd way to do default logic) model = Model() # default penman model, same as load(..., model=None) else: # False model = noop_model # Load the data out = penman.load(source=source, model=model) # Remove or replace the wiki tags if remove_wiki == 'remove': for i in range(len(out)): out[i] = _remove_wiki(out[i]) elif remove_wiki == 'replace': for i in range(len(out)): out[i] = _replace_wiki(out[i]) return out
def test_errors(self, mini_amr): m = Model() a = Model.from_dict(mini_amr) # basic roles g = Graph([('a', ':instance', 'alpha')]) assert m.errors(g) == {} g = Graph([('a', ':instance', 'alpha'), ('a', ':mod', '1')]) assert m.errors(g) == {('a', ':mod', '1'): ['invalid role']} assert a.errors(g) == {} # regex role names g = Graph([('n', ':instance', 'name'), ('n', ':op1', 'Foo'), ('n', ':op2', 'Bar')]) assert a.errors(g) == {} # disconnected graph g = Graph([('a', ':instance', 'alpha'), ('b', ':instance', 'beta')]) assert m.errors(g) == {('b', ':instance', 'beta'): ['unreachable']} assert a.errors(g) == {('b', ':instance', 'beta'): ['unreachable']}
def test_dereify(self, mini_amr): # (a :ARG1-of (_ / age-01 :ARG2 b)) -> (a :age b) t1 = ('_', ':instance', 'have-mod-91') t1b = ('_', ':instance', 'chase-01') t2 = ('_', ':ARG1', 'a') t3 = ('_', ':ARG2', 'b') m = Model() with pytest.raises(TypeError): m.dereify(t1) with pytest.raises(TypeError): m.dereify(t1, t2) with pytest.raises(ModelError): m.dereify(t1, t2, t3) m = Model.from_dict(mini_amr) assert m.dereify(t1, t2, t3) == ('a', ':mod', 'b') assert m.dereify(t1, t3, t2) == ('a', ':mod', 'b') with pytest.raises(ModelError): m.dereify(t1b, t2, t3)
def test_reify(self, mini_amr): m = Model() with pytest.raises(ModelError): m.reify(('a', ':ARG0', 'b')) with pytest.raises(ModelError): m.reify(('a', ':accompanier', 'b')) with pytest.raises(ModelError): m.reify(('a', ':domain', 'b')) with pytest.raises(ModelError): m.reify(('a', ':mod', 'b')) m = Model.from_dict(mini_amr) with pytest.raises(ModelError): m.reify(('a', ':ARG0', 'b')) assert m.reify( ('a', ':accompanier', 'b')) == (('_', ':ARG0', 'a'), ('_', ':instance', 'accompany-01'), ('_', ':ARG1', 'b')) with pytest.raises(ModelError): assert m.reify(('a', ':domain', 'b')) assert m.reify( ('a', ':mod', 'b')) == (('_', ':ARG1', 'a'), ('_', ':instance', 'have-mod-91'), ('_', ':ARG2', 'b')) # ensure unique ids if variables is specified assert m.reify(('a', ':mod', 'b'), variables={'a', 'b', '_'}) == (('_2', ':ARG1', 'a'), ('_2', ':instance', 'have-mod-91'), ('_2', ':ARG2', 'b'))
from penman import layout from penman.layout import ( interpret, rearrange, configure, reconfigure, get_pushed_variable, appears_inverted, node_contexts, ) random.seed(1) codec = PENMANCodec() model = Model() @pytest.fixture(scope='module') def amr_model(mini_amr): return Model.from_dict(mini_amr) def test_interpret(amr_model): t = codec.parse('(a / A)') assert interpret(t) == Graph([('a', ':instance', 'A')], top='a') t = codec.parse('(a / A :consist-of (b / B))') assert interpret(t) == Graph( [('a', ':instance', 'A'), ('b', ':consist', 'a'),
def test_has_role(self, mini_amr): m = Model() assert not m.has_role('') assert m.has_role(m.concept_role) assert not m.has_role(':ARG0') assert not m.has_role(':ARG0-of') m = Model.from_dict(mini_amr) assert not m.has_role('') assert m.has_role(m.concept_role) assert m.has_role(':ARG0') assert m.has_role(':ARG0-of') assert m.has_role(':mod') assert m.has_role(':mod-of') assert not m.has_role(':consist') assert m.has_role(':consist-of') assert m.has_role(':consist-of-of') assert not m.has_role(':fake') assert m.has_role(':op1') assert m.has_role(':op10') assert m.has_role(':op9999') assert not m.has_role(':op[0-9]+')
def test_is_role_reifiable(self, mini_amr): m = Model() assert not m.is_role_reifiable(':ARG0') assert not m.is_role_reifiable(':accompanier') assert not m.is_role_reifiable(':domain') assert not m.is_role_reifiable(':mod') m = Model.from_dict(mini_amr) assert not m.is_role_reifiable(':ARG0') assert m.is_role_reifiable(':accompanier') assert not m.is_role_reifiable(':domain') assert m.is_role_reifiable(':mod')
def test_from_dict(self, mini_amr): assert Model.from_dict(mini_amr) == Model( roles=mini_amr['roles'], normalizations=mini_amr['normalizations'], reifications=mini_amr['reifications'])
def test_canonicalize(self, mini_amr): m = Model() assert m.canonicalize(('a', ':ARG0', 'b')) == ('a', ':ARG0', 'b') assert m.canonicalize(('a', ':ARG0-of', 'b')) == ('a', ':ARG0-of', 'b') assert m.canonicalize(('a', ':ARG0-of-of', 'b')) == ('a', ':ARG0', 'b') assert m.canonicalize(('a', ':consist', 'b')) == ('a', ':consist', 'b') assert m.canonicalize( ('a', ':consist-of', 'b')) == ('a', ':consist-of', 'b') assert m.canonicalize( ('a', ':consist-of-of', 'b')) == ('a', ':consist', 'b') assert m.canonicalize(('a', ':mod', 'b')) == ('a', ':mod', 'b') assert m.canonicalize(('a', ':mod-of', 'b')) == ('a', ':mod-of', 'b') assert m.canonicalize(('a', ':domain', 'b')) == ('a', ':domain', 'b') assert m.canonicalize( ('a', ':domain-of', 'b')) == ('a', ':domain-of', 'b') # without : assert m.canonicalize(('a', 'ARG0', 'b')) == ('a', ':ARG0', 'b') assert m.canonicalize(('a', 'ARG0-of', 'b')) == ('a', ':ARG0-of', 'b') assert m.canonicalize(('a', 'ARG0-of-of', 'b')) == ('a', ':ARG0', 'b') m = Model.from_dict(mini_amr) assert m.canonicalize(('a', ':ARG0', 'b')) == ('a', ':ARG0', 'b') assert m.canonicalize(('a', ':ARG0-of', 'b')) == ('a', ':ARG0-of', 'b') assert m.canonicalize(('a', ':ARG0-of-of', 'b')) == ('a', ':ARG0', 'b') assert m.canonicalize( ('a', ':consist', 'b')) == ('a', ':consist-of-of', 'b') assert m.canonicalize( ('a', ':consist-of', 'b')) == ('a', ':consist-of', 'b') assert m.canonicalize( ('a', ':consist-of-of', 'b')) == ('a', ':consist-of-of', 'b') assert m.canonicalize(('a', ':mod', 'b')) == ('a', ':mod', 'b') assert m.canonicalize(('a', ':mod-of', 'b')) == ('a', ':domain', 'b') assert m.canonicalize(('a', ':domain', 'b')) == ('a', ':domain', 'b') assert m.canonicalize(('a', ':domain-of', 'b')) == ('a', ':mod', 'b') # without : assert m.canonicalize( ('a', 'consist', 'b')) == ('a', ':consist-of-of', 'b') assert m.canonicalize( ('a', 'consist-of', 'b')) == ('a', ':consist-of', 'b') assert m.canonicalize( ('a', 'consist-of-of', 'b')) == ('a', ':consist-of-of', 'b')
def amr_model(mini_amr): return Model.from_dict(mini_amr)
from typing import List from penman import load as load_, Graph, Triple from penman import loads as loads_ from penman import encode as encode_ from penman.model import Model from penman.models.noop import NoOpModel from penman.models import amr import penman import logging op_model = Model() noop_model = NoOpModel() amr_model = amr.model DEFAULT = op_model # Mute loggers penman.layout.logger.setLevel(logging.CRITICAL) penman._parse.logger.setLevel(logging.CRITICAL) def _get_model(dereify): if dereify is None: return DEFAULT elif dereify: return op_model else: return noop_model def _remove_wiki(graph):
def test__init__(self, mini_amr): m = Model() assert len(m.roles) == 0 m = Model(roles=mini_amr['roles']) assert len(m.roles) == 7
def test_is_role_inverted(self, mini_amr): m = Model() assert m.is_role_inverted(':ARG0-of') assert m.is_role_inverted(':-of') assert not m.is_role_inverted(':ARG0') assert not m.is_role_inverted(':') assert m.is_role_inverted(':consist-of') # # without : # assert m.is_role_inverted('ARG0-of') # assert not m.is_role_inverted('ARG0') m = Model.from_dict(mini_amr) assert m.is_role_inverted(':mod-of') assert m.is_role_inverted(':domain-of') assert not m.is_role_inverted(':mod') assert not m.is_role_inverted(':domain') assert m.is_role_inverted(':consist-of-of') assert not m.is_role_inverted(':consist-of')
from typing import Union, Mapping, Callable, Any, List, Set, cast import copy import logging from penman.exceptions import LayoutError from penman.types import Variable, BasicTriple from penman.epigraph import Epidatum from penman.surface import (Alignment, RoleAlignment) from penman.tree import (Tree, Node, Branch, is_atomic) from penman.graph import (Graph, CONCEPT_ROLE) from penman.model import Model logger = logging.getLogger(__name__) _default_model = Model() _Nodemap = Mapping[Variable, Union[Node, None]] # Epigraphical markers class LayoutMarker(Epidatum): """Epigraph marker for layout choices.""" class Push(LayoutMarker): """Epigraph marker to indicate a new node context.""" __slots__ = 'variable',
def test_invert_role(self, mini_amr): m = Model() assert m.invert_role(':ARG0') == ':ARG0-of' assert m.invert_role(':ARG0-of') == ':ARG0' assert m.invert_role(':consist-of') == ':consist' assert m.invert_role(':mod') == ':mod-of' assert m.invert_role(':domain') == ':domain-of' # # without : # assert m.invert_role('ARG0') == 'ARG0-of' # assert m.invert_role('ARG0-of') == 'ARG0' m = Model.from_dict(mini_amr) assert m.invert_role(':ARG0') == ':ARG0-of' assert m.invert_role(':ARG0-of') == ':ARG0' assert m.invert_role(':consist-of') == ':consist-of-of' assert m.invert_role(':mod') == ':mod-of' assert m.invert_role(':domain') == ':domain-of'
def __init__(self, model: Model = None): if model is None: model = Model() self.model = model
def test_invert(self, mini_amr): m = Model() assert m.invert(('a', ':ARG0', 'b')) == ('b', ':ARG0-of', 'a') assert m.invert(('a', ':ARG0-of', 'b')) == ('b', ':ARG0', 'a') assert m.invert(('a', ':consist-of', 'b')) == ('b', ':consist', 'a') assert m.invert(('a', ':mod', 'b')) == ('b', ':mod-of', 'a') assert m.invert(('a', ':domain', 'b')) == ('b', ':domain-of', 'a') # # without : # assert m.invert(('a', 'ARG0', 'b')) == ('b', 'ARG0-of', 'a') # assert m.invert(('a', 'ARG0-of', 'b')) == ('b', 'ARG0', 'a') m = Model.from_dict(mini_amr) assert m.invert(('a', ':ARG0', 'b')) == ('b', ':ARG0-of', 'a') assert m.invert(('a', ':ARG0-of', 'b')) == ('b', ':ARG0', 'a') assert m.invert( ('a', ':consist-of', 'b')) == ('b', ':consist-of-of', 'a') assert m.invert(('a', ':mod', 'b')) == ('b', ':mod-of', 'a') assert m.invert(('a', ':domain', 'b')) == ('b', ':domain-of', 'a')
(":meaning", "mean-01", ":ARG1", ":ARG2"), (":mod", "have-mod-91", ":ARG1", ":ARG2"), (":name", "have-name-91", ":ARG1", ":ARG2"), (":ord", "have-ord-91", ":ARG1", ":ARG2"), (":part", "have-part-91", ":ARG1", ":ARG2"), (":polarity", "have-polarity-91", ":ARG1", ":ARG2"), (":poss", "own-01", ":ARG0", ":ARG1"), (":poss", "have-03", ":ARG0", ":ARG1"), (":purpose", "have-purpose-91", ":ARG1", ":ARG2"), (":role", "have-org-role-91", ":ARG0", ":ARG2"), (":source", "be-from-91", ":ARG1", ":ARG2"), (":subevent", "have-subevent-91", ":ARG1", ":ARG2"), (":subset", "include-91", ":ARG2", ":ARG1"), (":superset", "include-91", ":ARG1", ":ARG2"), (":time", "be-temporally-at-91", ":ARG1", ":ARG2"), (":topic", "concern-02", ":ARG0", ":ARG1"), (":value", "have-value-91", ":ARG1", ":ARG2"), (":quant", "have-quant-91", ":ARG1", ":ARG2"), ] #: The AMR model is an instance of :class:`~penman.model.Model` using #: the roles, normalizations, and reifications defined in this module. model = Model( top_variable='top', top_role=':TOP', concept_role=':instance', roles=roles, normalizations=normalizations, reifications=reifications, )
from penman.model import Model from penman.models.amr import model as amr_model from penman.codec import PENMANCodec from penman.transform import ( canonicalize_roles, reify_edges, dereify_edges, reify_attributes, indicate_branches, ) def_model = Model() def_codec = PENMANCodec(model=def_model) amr_codec = PENMANCodec(model=amr_model) def test_canonicalize_roles_default_codec(): parse = def_codec.parse norm = lambda t: canonicalize_roles(t, def_model) format = lambda t: def_codec.format(t, indent=None) t = norm(parse('(a / alpha :ARG1 (b / beta))')) assert format(t) == '(a / alpha :ARG1 (b / beta))' t = norm(parse('(a / alpha :ARG1-of-of (b / beta))')) assert format(t) == '(a / alpha :ARG1 (b / beta))' t = norm(parse('(a / alpha :mod-of (b / beta))')) assert format(t) == '(a / alpha :mod-of (b / beta))'
def test_canonicalize_role(self, mini_amr): m = Model() assert m.canonicalize_role(':ARG0') == ':ARG0' assert m.canonicalize_role(':ARG0-of') == ':ARG0-of' assert m.canonicalize_role(':ARG0-of-of') == ':ARG0' assert m.canonicalize_role(':consist') == ':consist' assert m.canonicalize_role(':consist-of') == ':consist-of' assert m.canonicalize_role(':consist-of-of') == ':consist' assert m.canonicalize_role(':mod') == ':mod' assert m.canonicalize_role(':mod-of') == ':mod-of' assert m.canonicalize_role(':domain') == ':domain' assert m.canonicalize_role(':domain-of') == ':domain-of' # without : assert m.canonicalize_role('ARG0') == ':ARG0' assert m.canonicalize_role('ARG0-of') == ':ARG0-of' assert m.canonicalize_role('ARG0-of-of') == ':ARG0' m = Model.from_dict(mini_amr) assert m.canonicalize_role(':ARG0') == ':ARG0' assert m.canonicalize_role(':ARG0-of') == ':ARG0-of' assert m.canonicalize_role(':ARG0-of-of') == ':ARG0' assert m.canonicalize_role(':consist') == ':consist-of-of' assert m.canonicalize_role(':consist-of') == ':consist-of' assert m.canonicalize_role(':consist-of-of') == ':consist-of-of' assert m.canonicalize_role(':mod') == ':mod' assert m.canonicalize_role(':mod-of') == ':domain' assert m.canonicalize_role(':domain') == ':domain' assert m.canonicalize_role(':domain-of') == ':mod' # without : assert m.canonicalize_role('consist') == ':consist-of-of' assert m.canonicalize_role('consist-of') == ':consist-of' assert m.canonicalize_role('consist-of-of') == ':consist-of-of'