Repository: delph-in/pydelphin

Issue:
Predicate terminology is outdated
PyDelphin still refers to "string" and "grammar" predicates, where these should be "surface" and "abstract" ("string" is compared to "type", not "grammar"/"abstract").

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c622845..d728f60 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -31,6 +31,14 @@
 * Converting to PENMAN via the `convert` command should no longer crash for
   disconnected graphs, but print a log message to stderr, print a blank line
   to stdout, and then continue (#161)
+* Updated the docstrings for `delphin.mrs.xmrs.Xmrs.args()`, 
+  `delphin.mrs.xmrs.Xmrs.outgoing_args()`, and `delphin.mrs.xmrs.Xmrs.incoming_args()`,
+  from "DMRS-style undirected links" to "MOD/EQ links" and updated the Return
+  value of `Xmrs.args()` and `Xmrs.outgoing_args` from `{nodeid: {}}` to 
+  `{role: tgt}`(#133)
+* `delphin.mrs.compare.isomorphic()` compares predicates using a normalized form
+* Updated the code and the docstrings for references to 'string' and 'grammar'
+  predicates to refer to 'surface' and 'abstract' predicates (#117)
 
 ### Deprecated
 
diff --git a/delphin/mrs/compare.py b/delphin/mrs/compare.py
index edf55ac..268773a 100644
--- a/delphin/mrs/compare.py
+++ b/delphin/mrs/compare.py
@@ -43,9 +43,9 @@ def _make_digraph(x, check_varprops):
     for ep in x.eps():
         nid, pred, args = ep[0], ep[1], ep[3]
         if CONSTARG_ROLE in args:
-            s = '{}({})'.format(pred.string, args[CONSTARG_ROLE])
+            s = '{}({})'.format(pred.short_form(), args[CONSTARG_ROLE])
         else:
-            s = pred.string
+            s = pred.short_form()
         dg.add_node(nid, sig=s)
         dg.add_edges_from((nid, var_id(val)) for role, val in args.items()
                           if role != CONSTARG_ROLE)
@@ -450,7 +450,7 @@ def _isomorphic_var_signature(vd, xmrs, check_varprops):
         else:
             for nid in refval:
                 pred = xmrs.pred(nid)
-                sig.append('%s:%s' % (pred.string, role))
+                sig.append('%s:%s' % (pred.short_form(), role))
 
     return ' '.join(sorted(sig))
 
diff --git a/delphin/mrs/components.py b/delphin/mrs/components.py
index 31deaae..deb4275 100644
--- a/delphin/mrs/components.py
+++ b/delphin/mrs/components.py
@@ -10,6 +10,7 @@ from collections import namedtuple, MutableMapping
 from itertools import starmap
 
 from delphin.exceptions import (XmrsError, XmrsStructureError)
+from delphin.util import deprecated
 from .config import (
     IVARG_ROLE, CONSTARG_ROLE, RSTR_ROLE,
     UNKNOWNSORT, HANDLESORT, CVARSORT, QUANTIFIER_POS,
@@ -446,8 +447,8 @@ class Pred(namedtuple('Pred', ('type', 'lemma', 'pos', 'sense', 'string'))):
 
     In PyDelphin, Preds are equivalent if they have the same lemma,
     pos, and sense, and are both abstract or both surface preds. Other
-    factors are ignored for comparison, such as their being string-,
-    grammar-, or real-preds, whether they are quoted or not, whether
+    factors are ignored for comparison, such as their being surface-,
+    abstract-, or real-preds, whether they are quoted or not, whether
     they end with `_rel` or not, or differences in capitalization.
     Hashed Pred objects (e.g., in a dict or set) also use the
     normalized form. However, unlike with equality comparisons,
@@ -455,7 +456,7 @@ class Pred(namedtuple('Pred', ('type', 'lemma', 'pos', 'sense', 'string'))):
 
     Args:
         type: the type of predicate; valid values are
-            Pred.GRAMMARPRED, Pred.REALPRED, and Pred.STRINGPRED,
+            Pred.ABSTRACT, Pred.REALPRED, and Pred.SURFACE,
             although in practice Preds are instantiated via
             classmethods that select the type
         lemma: the lemma of the predicate
@@ -464,8 +465,8 @@ class Pred(namedtuple('Pred', ('type', 'lemma', 'pos', 'sense', 'string'))):
     Returns:
         a Pred object
     Attributes:
-        type: predicate type (Pred.GRAMMARPRED, Pred.REALPRED, or
-            Pred.STRINGPRED)
+        type: predicate type (Pred.ABSTRACT, Pred.REALPRED, 
+        and Pred.SURFACE)
         lemma: lemma component of the predicate
         pos: part-of-speech component of the predicate
         sense: sense component of the predicate
@@ -475,9 +476,9 @@ class Pred(namedtuple('Pred', ('type', 'lemma', 'pos', 'sense', 'string'))):
         capitalization doesn't matter. In addition, preds may be
         compared directly to their string representations:
 
-        >>> p1 = Pred.stringpred('_dog_n_1_rel')
+        >>> p1 = Pred.surface('_dog_n_1_rel')
         >>> p2 = Pred.realpred(lemma='dog', pos='n', sense='1')
-        >>> p3 = Pred.grammarpred('dog_n_1_rel')
+        >>> p3 = Pred.abstract('dog_n_1_rel')
         >>> p1 == p2
         True
         >>> p1 == '_dog_n_1_rel'
@@ -493,15 +494,15 @@ class Pred(namedtuple('Pred', ('type', 'lemma', 'pos', 'sense', 'string'))):
         re.IGNORECASE
     )
     # Pred types (used mainly in input/output, not internally in pyDelphin)
-    GRAMMARPRED = 0  # only a string allowed (quoted or not)
+    ABSTRACT = GRAMMARPRED = 0  # only a string allowed (quoted or not)
     REALPRED = 1  # may explicitly define lemma, pos, sense
-    STRINGPRED = 2  # quoted string form of realpred
+    SURFACE = STRINGPRED = 2  # quoted string form of realpred
 
     def __eq__(self, other):
         if other is None:
             return False
         if not isinstance(other, Pred):
-            other = Pred.stringpred(other)
+            other = Pred.surface(other)
         return self.short_form().lower() == other.short_form().lower()
 
     def __str__ (self):
@@ -514,24 +515,42 @@ class Pred(namedtuple('Pred', ('type', 'lemma', 'pos', 'sense', 'string'))):
         return hash(self.short_form())
 
     @classmethod
-    def stringpred(cls, predstr):
+    @deprecated(final_version='1.0.0', alternative='Pred.surface()')
+    def stringpred(cls, predstr): 
+        """Instantiate a Pred from its quoted string representation."""
+        return cls.surface(predstr)
+
+    @classmethod
+    def surface(cls, predstr):
         """Instantiate a Pred from its quoted string representation."""
         lemma, pos, sense, _ = split_pred_string(predstr)
-        return cls(Pred.STRINGPRED, lemma, pos, sense, predstr)
+        return cls(Pred.SURFACE, lemma, pos, sense, predstr)
 
     @classmethod
+    @deprecated(final_version='1.0.0', alternative='Pred.abstract()')
     def grammarpred(cls, predstr):
+        """Instantiate a Pred from its symbol string."""
+        return cls.abstract(predstr)
+
+    @classmethod
+    def abstract(cls, predstr):
         """Instantiate a Pred from its symbol string."""
         lemma, pos, sense, _ = split_pred_string(predstr)
-        return cls(Pred.GRAMMARPRED, lemma, pos, sense, predstr)
+        return cls(Pred.ABSTRACT, lemma, pos, sense, predstr)
+
+    @classmethod
+    @deprecated(final_version='1.0.0', alternative='Pred.surface_or_abstract()')
+    def string_or_grammar_pred(cls, predstr):
+        """Instantiate a Pred from either its surface or abstract symbol."""
+        return cls.surface_or_abstract(predstr)
 
-    @staticmethod
-    def string_or_grammar_pred(predstr):
-        """Instantiate a Pred from either its string or grammar symbol."""
+    @classmethod
+    def surface_or_abstract(cls, predstr):
+        """Instantiate a Pred from either its surface or abstract symbol."""
         if predstr.strip('"').lstrip("'").startswith('_'):
-            return Pred.stringpred(predstr)
+            return cls.surface(predstr)
         else:
-            return Pred.grammarpred(predstr)
+            return cls.abstract(predstr)
 
     @classmethod
     def realpred(cls, lemma, pos, sense=None):
@@ -554,7 +573,7 @@ class Pred(namedtuple('Pred', ('type', 'lemma', 'pos', 'sense', 'string'))):
 
         Example:
 
-            >>> p = Pred.stringpred('"_cat_n_1_rel"')
+            >>> p = Pred.surface('"_cat_n_1_rel"')
             >>> p.short_form()
             '_cat_n_1'
         """
diff --git a/delphin/mrs/dmrx.py b/delphin/mrs/dmrx.py
index 86a79a4..18cb1ed 100644
--- a/delphin/mrs/dmrx.py
+++ b/delphin/mrs/dmrx.py
@@ -166,7 +166,7 @@ def _decode_pred(elem):
     #           sense CDATA #IMPLIED >
     # <!ELEMENT gpred (#PCDATA)>
     if elem.tag == 'gpred':
-        return Pred.grammarpred(elem.text)
+        return Pred.abstract(elem.text)
     elif elem.tag == 'realpred':
         return Pred.realpred(elem.get('lemma'),
                              elem.get('pos') or None,
@@ -267,10 +267,10 @@ def _encode_node(node, properties):
 
 
 def _encode_pred(pred):
-    if pred.type == Pred.GRAMMARPRED:
+    if pred.type == Pred.ABSTRACT:
         e = etree.Element('gpred')
         e.text = pred.string.strip('"\'')
-    elif pred.type in (Pred.REALPRED, Pred.STRINGPRED):
+    elif pred.type in (Pred.REALPRED, Pred.SURFACE):
         attributes = {}
         attributes['lemma'] = pred.lemma
         if pred.pos is None:
diff --git a/delphin/mrs/eds.py b/delphin/mrs/eds.py
index 280b5fa..f99799b 100644
--- a/delphin/mrs/eds.py
+++ b/delphin/mrs/eds.py
@@ -163,7 +163,7 @@ class Eds(object):
         """
         Decode a dictionary, as from :meth:`to_dict`, into an Eds object.
         """
-        makepred, charspan = Pred.string_or_grammar_pred, Lnk.charspan
+        makepred, charspan = Pred.surface_or_abstract, Lnk.charspan
         top = d.get('top')
         nodes, edges = [], []
         for nid, node in d.get('nodes', {}).items():
@@ -231,7 +231,7 @@ class Eds(object):
                 nids.append(src)
                 nd[src] = {'pred': None, 'lnk': None, 'carg': None, 'si': []}
             if rel == 'predicate':
-                nd[src]['pred'] = Pred.string_or_grammar_pred(tgt)
+                nd[src]['pred'] = Pred.surface_or_abstract(tgt)
             elif rel == 'lnk':
                 cfrom, cto = tgt.strip('"<>').split(':')
                 nd[src]['lnk'] = Lnk.charspan(int(cfrom), int(cto))
@@ -501,7 +501,7 @@ _COMMA   = regex(r',\s*')
 _SPACES  = regex(r'\s+', value=Ignore)
 _SYMBOL  = regex(r'[-+\w]+')
 _PRED    = regex(r'((?!<-?\d|\("|\{|\[)\w)+',
-                 value=Pred.string_or_grammar_pred)
+                 value=Pred.surface_or_abstract)
 _EDS     = nt('EDS', value=_make_eds)
 _TOP     = opt(nt('TOP'), default=None)
 _TOPID   = opt(_SYMBOL, default=None)
diff --git a/delphin/mrs/mrx.py b/delphin/mrs/mrx.py
index eadcbfd..d5ebfa1 100644
--- a/delphin/mrs/mrx.py
+++ b/delphin/mrs/mrx.py
@@ -217,9 +217,9 @@ def _decode_pred(elem):
     #           pos (v|n|j|r|p|q|c|x|u|a|s) #REQUIRED
     #           sense CDATA #IMPLIED >
     if elem.tag == 'pred':
-        return Pred.grammarpred(elem.text)
+        return Pred.abstract(elem.text)
     elif elem.tag == 'spred':
-        return Pred.stringpred(elem.text)
+        return Pred.surface(elem.text)
     elif elem.tag == 'realpred':
         return Pred.realpred(elem.get('lemma'),
                              elem.get('pos') or None,
@@ -364,10 +364,10 @@ def _encode_ep(ep, varprops=None):
 
 def _encode_pred(pred):
     p = None
-    if pred.type == Pred.GRAMMARPRED:
+    if pred.type == Pred.ABSTRACT:
         p = etree.Element('pred')
         p.text = pred.string
-    elif pred.type == Pred.STRINGPRED:
+    elif pred.type == Pred.SURFACE:
         p = etree.Element('spred')
         p.text = pred.string
     elif pred.type == Pred.REALPRED:
diff --git a/delphin/mrs/simplemrs.py b/delphin/mrs/simplemrs.py
index 40f936a..ab194b7 100644
--- a/delphin/mrs/simplemrs.py
+++ b/delphin/mrs/simplemrs.py
@@ -280,7 +280,7 @@ def _read_ep(tokens, nid, vars_):
     _var_re = var_re
     # begin parsing
     _read_literals(tokens, '[')
-    pred = Pred.string_or_grammar_pred(tokens.popleft())
+    pred = Pred.surface_or_abstract(tokens.popleft())
     lnk = _read_lnk(tokens)
     surface = label = None
     if tokens[0].startswith('"'):
diff --git a/delphin/mrs/xmrs.py b/delphin/mrs/xmrs.py
index 7dd411e..9b6d7a2 100644
--- a/delphin/mrs/xmrs.py
+++ b/delphin/mrs/xmrs.py
@@ -541,7 +541,7 @@ class Xmrs(_LnkMixin):
                 rank[n] = 0
             elif pred.is_quantifier():
                 rank[n] = 0
-            elif pred.type == Pred.GRAMMARPRED:
+            elif pred.type == Pred.ABSTRACT:
                 rank[n] = 2
             else:
                 rank[n] = 1
@@ -746,7 +746,7 @@ class Mrs(Xmrs):
     >>>     top='h0',
     >>>     index='e2',
     >>>     rels=[ElementaryPredication(
-    >>>         Pred.stringpred('_rain_v_1_rel'),
+    >>>         Pred.surface('_rain_v_1_rel'),
     >>>         label='h1',
     >>>         args={'ARG0': 'e2'},
     >>>         vars={'e2': {'SF': 'prop-or-ques', 'TENSE': 'present'}}
@@ -822,7 +822,7 @@ class Mrs(Xmrs):
         def _ep(ep):
             return ElementaryPredication(
                 nodeid=None,
-                pred=Pred.string_or_grammar_pred(ep['predicate']),
+                pred=Pred.surface_or_abstract(ep['predicate']),
                 label=ep['label'],
                 args=ep.get('arguments', {}),
                 lnk=_lnk(ep.get('lnk')),
@@ -882,7 +882,7 @@ def Rmrs(top=None, index=None, xarg=None,
     >>>     index='e2',
     >>>     eps=[ElementaryPredication(
     >>>         10000,
-    >>>         Pred.stringpred('_rain_v_1_rel'),
+    >>>         Pred.surface('_rain_v_1_rel'),
     >>>         'h1'
     >>>     )],
     >>>     args={10000: {'ARG0': 'e2'}},
@@ -934,7 +934,7 @@ class Dmrs(Xmrs):
 
     Example:
 
-    >>> rain = Node(10000, Pred.stringpred('_rain_v_1_rel'),
+    >>> rain = Node(10000, Pred.surface('_rain_v_1_rel'),
     >>>             sortinfo={'cvarsort': 'e'})
     >>> ltop_link = Link(0, 10000, post='H')
     >>> d = Dmrs([rain], [ltop_link])
@@ -1056,7 +1056,7 @@ class Dmrs(Xmrs):
         def _node(obj):
             return Node(
                 obj.get('nodeid'),
-                Pred.string_or_grammar_pred(obj.get('predicate')),
+                Pred.surface_or_abstract(obj.get('predicate')),
                 sortinfo=obj.get('sortinfo'),
                 lnk=_lnk(obj.get('lnk')),
                 surface=obj.get('surface'),
@@ -1120,7 +1120,7 @@ class Dmrs(Xmrs):
                 nids.append(src)
                 nd[src] = {'pred': None, 'lnk': None, 'carg': None, 'si': []}
             if rel == 'predicate':
-                nd[src]['pred'] = Pred.string_or_grammar_pred(tgt)
+                nd[src]['pred'] = Pred.surface_or_abstract(tgt)
             elif rel == 'lnk':
                 cfrom, cto = tgt.strip('"<>').split(':')
                 nd[src]['lnk'] = Lnk.charspan(int(cfrom), int(cto))


Test patch:
diff --git a/tests/mrs_Dmrs_test.py b/tests/mrs_Dmrs_test.py
index 9d2980b..de621ab 100644
--- a/tests/mrs_Dmrs_test.py
+++ b/tests/mrs_Dmrs_test.py
@@ -8,7 +8,7 @@ from delphin.mrs.config import UNKNOWNSORT
 #from delphin.mrs import simplemrs  # for convenience in later tests
 from delphin.exceptions import XmrsError
 
-sp = Pred.stringpred
+sp = Pred.surface
 
 
 # for convenience
diff --git a/tests/mrs_Mrs_test.py b/tests/mrs_Mrs_test.py
index 00fe320..38d1588 100644
--- a/tests/mrs_Mrs_test.py
+++ b/tests/mrs_Mrs_test.py
@@ -16,7 +16,7 @@ from delphin.mrs import Mrs
 #from delphin.mrs import simplemrs  # for convenience in later tests
 from delphin.exceptions import XmrsError
 
-sp = Pred.stringpred
+sp = Pred.surface
 
 
 # for convenience
diff --git a/tests/mrs_components_test.py b/tests/mrs_components_test.py
index fbfecd1..7238afe 100644
--- a/tests/mrs_components_test.py
+++ b/tests/mrs_components_test.py
@@ -10,7 +10,7 @@ from delphin.mrs.components import (
     Pred, split_pred_string, is_valid_pred_string, normalize_pred_string,
     Node, ElementaryPredication as EP
 )
-spred = Pred.stringpred
+spred = Pred.surface
 from delphin.mrs.xmrs import Xmrs
 from delphin.mrs.config import (
     CVARSORT, IVARG_ROLE, CONSTARG_ROLE, RSTR_ROLE,
@@ -338,27 +338,27 @@ def test_hcons():
 
 class TestPred():
     def testGpred(self):
-        p = Pred.grammarpred('pron_rel')
-        assert p.type == Pred.GRAMMARPRED
+        p = Pred.abstract('pron_rel')
+        assert p.type == Pred.ABSTRACT
         assert p.string == 'pron_rel'
         assert p.lemma == 'pron'
         assert p.pos == None
         assert p.sense == None
         assert p.short_form() == 'pron'
-        p = Pred.grammarpred('udef_q_rel')
+        p = Pred.abstract('udef_q_rel')
         assert p.string == 'udef_q_rel'
         assert p.lemma == 'udef'
         assert p.pos == 'q'
         assert p.sense == None
         assert p.short_form() == 'udef_q'
-        p = Pred.grammarpred('udef_q')
+        p = Pred.abstract('udef_q')
         assert p.string == 'udef_q'
         assert p.lemma == 'udef'
         assert p.pos == 'q'
         assert p.sense == None
         assert p.short_form() == 'udef_q'
-        p = Pred.grammarpred('abc_def_ghi_rel')
-        assert p.type == Pred.GRAMMARPRED
+        p = Pred.abstract('abc_def_ghi_rel')
+        assert p.type == Pred.ABSTRACT
         assert p.string == 'abc_def_ghi_rel'
         # pos must be a single character, so we get abc_def, ghi, rel
         assert p.lemma == 'abc_def'
@@ -369,28 +369,28 @@ class TestPred():
 
     def testSpred(self):
         p = spred('_dog_n_rel')
-        assert p.type == Pred.STRINGPRED
+        assert p.type == Pred.SURFACE
         assert p.string == '_dog_n_rel'
         assert p.lemma == 'dog'
         assert p.pos == 'n'
         assert p.sense == None
         assert p.short_form() == '_dog_n'
         p = spred('_犬_n_rel')
-        assert p.type == Pred.STRINGPRED
+        assert p.type == Pred.SURFACE
         assert p.string == '_犬_n_rel'
         assert p.lemma == '犬'
         assert p.pos == 'n'
         assert p.sense == None
         assert p.short_form() == '_犬_n'
         p = spred('"_dog_n_1_rel"')
-        assert p.type == Pred.STRINGPRED
+        assert p.type == Pred.SURFACE
         assert p.string == '"_dog_n_1_rel"'
         assert p.lemma == 'dog'
         assert p.pos == 'n'
         assert p.sense == '1'
         assert p.short_form() == '_dog_n_1'
         p = spred('"_dog_n_1"')
-        assert p.type == Pred.STRINGPRED
+        assert p.type == Pred.SURFACE
         assert p.string == '"_dog_n_1"'
         assert p.lemma == 'dog'
         assert p.pos == 'n'
@@ -398,7 +398,7 @@ class TestPred():
         assert p.short_form() == '_dog_n_1'
         # see https://github.com/delph-in/pydelphin/issues/129
         p = spred('_te_adjunct_rel')
-        assert p.type == Pred.STRINGPRED
+        assert p.type == Pred.SURFACE
         assert p.string == '_te_adjunct_rel'
         assert p.lemma == 'te'
         assert p.pos == None
@@ -410,11 +410,11 @@ class TestPred():
         #with pytest.raises(ValueError): spred('_dog_n_1_2_rel')
         repr(p)  # no error
 
-    def testStringOrGrammarPred(self):
-        p = Pred.string_or_grammar_pred('_dog_n_rel')
-        assert p.type == Pred.STRINGPRED
-        p = Pred.string_or_grammar_pred('pron_rel')
-        assert p.type == Pred.GRAMMARPRED
+    def testSurfaceOrAbstractPred(self):
+        p = Pred.surface_or_abstract('_dog_n_rel')
+        assert p.type == Pred.SURFACE
+        p = Pred.surface_or_abstract('pron_rel')
+        assert p.type == Pred.ABSTRACT
 
     def testRealPred(self):
         # basic, no sense arg
@@ -459,8 +459,8 @@ class TestPred():
         assert spred('_dog_n_rel') == '_dog_n_rel'
         assert '_dog_n_rel' == Pred.realpred(lemma='dog', pos='n')
         assert spred('"_dog_n_rel"') == spred("'_dog_n_rel")
-        assert Pred.grammarpred('pron_rel') == 'pron_rel'
-        assert Pred.string_or_grammar_pred('_dog_n_rel') != Pred.string_or_grammar_pred('dog_n_rel')
+        assert Pred.abstract('pron_rel') == 'pron_rel'
+        assert Pred.surface_or_abstract('_dog_n_rel') != Pred.surface_or_abstract('dog_n_rel')
         assert (spred('_dog_n_rel') == None) == False
         assert spred('_dog_n_1_rel') == spred('_Dog_N_1_rel')
         assert spred('_dog_n_1_rel') == spred('_dog_n_1')
diff --git a/tests/mrs_eds_test.py b/tests/mrs_eds_test.py
index ca73ea3..eb2ce90 100644
--- a/tests/mrs_eds_test.py
+++ b/tests/mrs_eds_test.py
@@ -70,7 +70,7 @@ def eds_it_rains():
         nodes=[
             Node(
                 'e2',
-                Pred.stringpred('"_rain_v_1_rel"'),
+                Pred.surface('"_rain_v_1_rel"'),
                 sortinfo={
                     'SF': 'prop', 'TENSE': 'pres', 'MOOD': 'indicative',
                     'PROG': '-', 'PERF': '-', CVARSORT: 'e'},
@@ -85,11 +85,11 @@ def eds_dogs_chase_Kim():
     return eds.Eds(
         top='e2',
         nodes=[
-            Node('_1', Pred.stringpred('udef_q_rel')),
-            Node('x4', Pred.stringpred('"_dog_n_1_rel"')),
-            Node('e2', Pred.stringpred('"_chase_v_1_rel"')),
-            Node('_2', Pred.stringpred('proper_q_rel')),
-            Node('x6', Pred.stringpred('named_rel'), carg='Kim')
+            Node('_1', Pred.surface('udef_q_rel')),
+            Node('x4', Pred.surface('"_dog_n_1_rel"')),
+            Node('e2', Pred.surface('"_chase_v_1_rel"')),
+            Node('_2', Pred.surface('proper_q_rel')),
+            Node('x6', Pred.surface('named_rel'), carg='Kim')
         ],
         edges=[
             ('_1', 'BV', 'x4'),
@@ -104,10 +104,10 @@ def eds_kim_probably_sleeps():
     return eds.Eds(
         top='e9',
         nodes=[
-            Node('_1', Pred.stringpred('proper_q_rel')),
-            Node('x3', Pred.stringpred('named_rel'), carg='Kim'),
-            Node('e9', Pred.stringpred('_probable_a_1_rel')),
-            Node('e2', Pred.stringpred('_sleep_v_1_rel')),        
+            Node('_1', Pred.surface('proper_q_rel')),
+            Node('x3', Pred.surface('named_rel'), carg='Kim'),
+            Node('e9', Pred.surface('_probable_a_1_rel')),
+            Node('e2', Pred.surface('_sleep_v_1_rel')),        
         ],
         edges=[
             ('_1', 'BV', 'x3'),
diff --git a/tests/mrs_isomorphism_test.py b/tests/mrs_isomorphism_test.py
new file mode 100644
index 0000000..7141200
--- /dev/null
+++ b/tests/mrs_isomorphism_test.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Thu Jul 26 17:33:39 2018
+
+@author: aymm
+"""
+
+from delphin.mrs import simplemrs, compare
+
+# s1 and s2 differ only by the presence of '_rel' at the end of the predicates
+ 
+s1 = '[ LTOP: h0 INDEX: e2 [ e SF: prop TENSE: past MOOD: indicative PROG: - PERF: - ] RELS: < [ proper_q<0:6> LBL: h4 ARG0: x3 [ x PERS: 3 NUM: sg IND: + ] RSTR: h5 BODY: h6 ]  [ named<0:6> LBL: h7 CARG: "Abrams" ARG0: x3 ]  [ _sleep_v_1<7:13> LBL: h1 ARG0: e2 ARG1: x3 ] > HCONS: < h0 qeq h1 h5 qeq h7 > ]'
+
+s2 = '[ LTOP: h0 INDEX: e2 [ e SF: prop TENSE: past MOOD: indicative PROG: - PERF: - ] RELS: < [ proper_q_rel<0:6> LBL: h4 ARG0: x3 [ x PERS: 3 NUM: sg IND: + ] RSTR: h5 BODY: h6 ]  [ named_rel<0:6> LBL: h7 CARG: "Abrams" ARG0: x3 ]  [ _sleep_v_1_rel<7:13> LBL: h1 ARG0: e2 ARG1: x3 ] > HCONS: < h0 qeq h1 h5 qeq h7 > ]'
+
+x1 = simplemrs.loads_one(s1)
+x2 = simplemrs.loads_one(s2)
+
+def test_isomorphic():
+    assert x1 == x2 # generally a stricter test than isomorphism
+    assert compare.isomorphic(x1, x2) 
+    assert compare.isomorphic(x1, x1) 
+    assert compare.isomorphic(x2, x2) 
diff --git a/tests/mrs_path_test.py b/tests/mrs_path_test.py
index a17b3d5..478f8e8 100644
--- a/tests/mrs_path_test.py
+++ b/tests/mrs_path_test.py
@@ -2,7 +2,7 @@
 from delphin.mrs import xmrs, path as mp
 from delphin.mrs.components import Pred, ElementaryPredication as EP
 
-sp = Pred.stringpred
+sp = Pred.surface
 
 def qeq(hi, lo): return (hi, 'qeq', lo)
 
diff --git a/tests/mrs_query_test.py b/tests/mrs_query_test.py
index b989959..4ce6e7a 100644
--- a/tests/mrs_query_test.py
+++ b/tests/mrs_query_test.py
@@ -3,7 +3,7 @@ from delphin.mrs.xmrs import Xmrs
 from delphin.mrs.components import ElementaryPredication as EP, Pred
 from delphin.mrs import query
 
-sp = Pred.stringpred
+sp = Pred.surface
 qeq = lambda hi, lo: (hi, 'qeq', lo)
 
 # "Cats are chased by big dogs." (reordered, but equivalent)
diff --git a/tests/mrs_xmrs_test.py b/tests/mrs_xmrs_test.py
index 4dc1635..41808c8 100644
--- a/tests/mrs_xmrs_test.py
+++ b/tests/mrs_xmrs_test.py
@@ -33,10 +33,10 @@ class TestXmrs():
         assert len(x.eps()) == 0
         # nodeid and pred
         with pytest.raises(XmrsError):
-            x.add_eps([(10000, Pred.stringpred('_v_v_rel'))])
+            x.add_eps([(10000, Pred.surface('_v_v_rel'))])
         assert len(x.eps()) == 0
         # nodeid, pred, and label (the minimum)
-        x.add_eps([(10000, Pred.stringpred('_v_v_rel'), 'h1')])
+        x.add_eps([(10000, Pred.surface('_v_v_rel'), 'h1')])
         # make sure it was entered correctly and is unchanged
         assert len(x.eps()) == 1
         assert x.eps()[0][0] == 10000
@@ -46,7 +46,7 @@ class TestXmrs():
 
         # nodeid, pred, label, and argdict
         x = Xmrs()
-        x.add_eps([(10000, Pred.stringpred('_v_v_rel'), 'h1', {})])
+        x.add_eps([(10000, Pred.surface('_v_v_rel'), 'h1', {})])
         assert len(x.eps()) == 1
         assert x.eps()[0][0] == 10000
         ep = x.ep(10000)
@@ -57,7 +57,7 @@ class TestXmrs():
 
         # cannot have more than one ep with the same nodeid
         with pytest.raises(XmrsError):
-            x.add_eps([(10000, Pred.stringpred('_n_n_rel'), 'h3', {})])
+            x.add_eps([(10000, Pred.surface('_n_n_rel'), 'h3', {})])
         assert len(x.eps()) == 1
 
     def test_add_hcons(self):
@@ -112,7 +112,7 @@ class TestXmrs():
         assert x.xarg == 'e0'
 
     def test_nodeid(self):
-        sp = Pred.stringpred
+        sp = Pred.surface
         x = Xmrs()
         with pytest.raises(KeyError):
             x.nodeid('e2')
@@ -128,7 +128,7 @@ class TestXmrs():
         assert x.nodeid('x4', quantifier=True) == 11
 
     def test_nodeids(self):
-        sp = Pred.stringpred
+        sp = Pred.surface
         x = Xmrs(eps=[(10, sp('_n_n_rel'), 'h3', {'ARG0': 'x4'})])
         assert x.nodeids() == [10]
         assert x.nodeids(ivs=['x4']) == [10]
@@ -143,7 +143,7 @@ class TestXmrs():
         assert sorted(x.nodeids(ivs=['x4'], quantifier=False)) == [10]
 
     def test_ep(self):
-        sp = Pred.stringpred
+        sp = Pred.surface
         x = Xmrs()
         with pytest.raises(TypeError):
             x.ep()
@@ -153,7 +153,7 @@ class TestXmrs():
         assert x.ep(10)[1] == sp('_n_n_rel')
 
     def test_eps(self):
-        sp = Pred.stringpred
+        sp = Pred.surface
         x = Xmrs()
         assert len(x.eps()) == 0
         x.add_eps([(10, sp('_n_n_rel'), 'h3', {'ARG0': 'x4'})])
@@ -236,7 +236,7 @@ class TestXmrs():
         assert len(x.icons(left='x7')) == 1
 
     def test_variables_and_properties(self):
-        sp = Pred.stringpred
+        sp = Pred.surface
         # variables can be passed in with properties
         x = Xmrs(vars={'x1':{'PERS':'3','NUM':'sg'}, 'e2':{'SF':'prop'}})
         assert len(x.variables()) == 2
@@ -287,11 +287,11 @@ class TestXmrs():
         # KeyError on bad nodeid
         with pytest.raises(KeyError): x.pred(10)
         # but otherwise preds can be retrieved by nodeid
-        x.add_eps([(10, Pred.stringpred('_n_n_rel'), 'h3', {'ARG0': 'x4'})])
+        x.add_eps([(10, Pred.surface('_n_n_rel'), 'h3', {'ARG0': 'x4'})])
         assert x.pred(10).string == '_n_n_rel'
 
     def test_preds(self):
-        sp = Pred.stringpred
+        sp = Pred.surface
         x = Xmrs(
             eps=[
                 (10, sp('_v_v_rel'), 'h3', {'ARG0': 'e2', 'ARG1': 'x4'}),
@@ -324,12 +324,12 @@ class TestXmrs():
 
     def test_label(self):
         # retrieve the label for a single ep, or KeyError if no such nodeid
-        x = Xmrs(eps=[(10, Pred.stringpred('_v_v_rel'), 'h3', {'ARG0': 'e2'})])
+        x = Xmrs(eps=[(10, Pred.surface('_v_v_rel'), 'h3', {'ARG0': 'e2'})])
         assert x.label(10) == 'h3'
         with pytest.raises(KeyError): x.label(11)
 
     def test_labels(self):
-        sp = Pred.stringpred
+        sp = Pred.surface
         # same as Xmrs.labels() but with a list of nodeids
         x = Xmrs(
             eps=[
@@ -347,7 +347,7 @@ class TestXmrs():
     def test_args(self):
         # return the argument dict of a nodeid, or KeyError for missing nodeid
         x = Xmrs(
-            eps=[(10, Pred.stringpred('_v_v_rel'), 'h3',
+            eps=[(10, Pred.surface('_v_v_rel'), 'h3',
                   {'ARG0': 'e2', 'ARG1': 'x4'})]
         )
         assert x.args(10) == {'ARG0': 'e2', 'ARG1': 'x4'}
@@ -356,11 +356,11 @@ class TestXmrs():
         x.args(10)['ARG1'] = 'x6'
         assert x.args(10)['ARG1'] == 'x4'
         # return empty arg dict for EP without specified args:
-        x = Xmrs(eps=[(10, Pred.stringpred('_v_v_rel'), 'h3')])
+        x = Xmrs(eps=[(10, Pred.surface('_v_v_rel'), 'h3')])
         assert x.args(10) == {}
 
     def test_outgoing_args(self):
-        sp = Pred.stringpred
+        sp = Pred.surface
         # Outgoing args are those that, from some start node, go to
         # another in some way. These ways include:
         # regular variable args
@@ -404,7 +404,7 @@ class TestXmrs():
     def test_incoming_args(self):
         # incoming_args() is like the reverse of outgoing_args(), but
         # now it's many-to-one instead of one-to-many
-        sp = Pred.stringpred
+        sp = Pred.surface
         x = Xmrs(
             eps=[
                 (10, sp('_v_v_rel'), 'h3', {'ARG0': 'e2'}),
@@ -479,7 +479,7 @@ class TestXmrs():
         assert 10001 not in x
         assert '10000' not in x
         assert '_v_v_rel' not in x
-        assert Pred.stringpred('_v_v_rel') not in x
+        assert Pred.surface('_v_v_rel') not in x
 
     def test_labelset(self): pass
     def test_labelset_heads(self): pass
@@ -493,9 +493,9 @@ class TestXmrs():
         with pytest.raises(XmrsError):
             Xmrs(hcons=[('h0', 'qeq', 'h1')]).is_connected()
         # just a pred is fine (even without ARG0)
-        x = Xmrs(eps=[(10, Pred.stringpred('_v_v_rel'), 'h1', {})])
+        x = Xmrs(eps=[(10, Pred.surface('_v_v_rel'), 'h1', {})])
         assert x.is_connected() == True
-        x = Xmrs(eps=[(10, Pred.stringpred('_v_v_rel'), 'h1',
+        x = Xmrs(eps=[(10, Pred.surface('_v_v_rel'), 'h1',
                        {'ARG0':'e2'})])
         assert x.is_connected() == True
         # disconnected top is fine
@@ -553,7 +553,7 @@ class TestXmrs():
         pass
 
     def test_subgraph(self):
-        nodes = [Node(1,Pred.stringpred('verb'))]
+        nodes = [Node(1,Pred.surface('verb'))]
         links = [Link(0,1,'','H')]
         graph = Dmrs(nodes,links)
         new_graph = graph.subgraph([1])