Skip to content

Commit 4d4056c

Browse files
committed
Add a RelabelNamedEdge.
This operation puts the named edge into the SemgrexMatcher after relabeling something. If the edge already exists, a duplicate is not created. Add a test of this operation
1 parent 8bdf456 commit 4d4056c

File tree

4 files changed

+192
-0
lines changed

4 files changed

+192
-0
lines changed

src/edu/stanford/nlp/semgraph/semgrex/SemgrexMatcher.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,10 @@ public Set<String> getEdgeNames() {
231231
return namesToEdges.keySet();
232232
}
233233

234+
public SemanticGraphEdge putNamedEdge(String name, SemanticGraphEdge edge) {
235+
return namesToEdges.put(name, edge);
236+
}
237+
234238
@Override
235239
public abstract String toString();
236240

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package edu.stanford.nlp.semgraph.semgrex.ssurgeon;
2+
3+
import java.io.StringWriter;
4+
5+
import edu.stanford.nlp.semgraph.semgrex.SemgrexMatcher;
6+
import edu.stanford.nlp.semgraph.SemanticGraph;
7+
import edu.stanford.nlp.semgraph.SemanticGraphEdge;
8+
9+
import edu.stanford.nlp.trees.EnglishGrammaticalRelations;
10+
import edu.stanford.nlp.trees.GrammaticalRelation;
11+
12+
/**
13+
* Given a named edge, change that edge from the SemanticGraph,
14+
* then put it back with the updated relation.
15+
*<br>
16+
* If an edge already exists with the new named relation,
17+
* <i>that</i> edge is deleted permanently. That way, named
18+
* references to the first edge should still work.
19+
*
20+
* @author John Bauer
21+
*
22+
*/
23+
public class RelabelNamedEdge extends SsurgeonEdit {
24+
public static final String LABEL = "relabelNamedEdge";
25+
26+
protected final String edgeName; // Name of the matched edge in the SemgrexPattern
27+
28+
protected final GrammaticalRelation relation; // Type of relation to add between these edges
29+
30+
public RelabelNamedEdge(String edgeName, GrammaticalRelation relation) {
31+
if (edgeName == null) {
32+
throw new SsurgeonParseException("RelabelNamedEdge created with no edge name!");
33+
}
34+
if (relation == null) {
35+
throw new SsurgeonParseException("RelabelNamedEdge created with no relation!");
36+
}
37+
38+
this.edgeName = edgeName;
39+
this.relation = relation;
40+
}
41+
42+
public static RelabelNamedEdge createEngRelabel(String edgeName, String relation) {
43+
GrammaticalRelation reln = EnglishGrammaticalRelations.valueOf(relation);
44+
return new RelabelNamedEdge(edgeName, reln);
45+
}
46+
47+
@Override
48+
public String toEditString() {
49+
StringWriter buf = new StringWriter();
50+
buf.write(LABEL); buf.write("\t");
51+
52+
buf.write(Ssurgeon.EDGE_NAME_ARG);buf.write(" ");
53+
buf.write(edgeName); buf.write("\t");
54+
55+
buf.write(Ssurgeon.RELN_ARG);buf.write(" ");
56+
buf.write(relation.toString()); buf.write("\t");
57+
58+
return buf.toString();
59+
}
60+
61+
/**
62+
* "Rename" the named edge by removing it and then recreating it
63+
*/
64+
@Override
65+
public boolean evaluate(SemanticGraph sg, SemgrexMatcher sm) {
66+
SemanticGraphEdge edge = sm.getEdge(edgeName);
67+
68+
if (edge != null) {
69+
boolean success = sg.removeEdge(edge);
70+
if (!success) {
71+
// maybe it was already removed somehow by a previous operation
72+
return false;
73+
}
74+
final SemanticGraphEdge newEdge;
75+
found: {
76+
for (SemanticGraphEdge existingEdge : sg.getAllEdges(edge.getSource(), edge.getTarget())) {
77+
if (existingEdge.getRelation().equals(this.relation)) {
78+
newEdge = existingEdge;
79+
break found;
80+
}
81+
}
82+
newEdge = new SemanticGraphEdge(edge.getSource(),
83+
edge.getTarget(),
84+
this.relation,
85+
edge.getWeight(),
86+
edge.isExtra());
87+
sg.addEdge(newEdge);
88+
}
89+
// whether we recreated a new edge with the new relation,
90+
// or found an existing edge with the relation we wanted,
91+
// update the named edge in the SemgrexMatcher so future
92+
// iterations have the name connected to the edge
93+
sm.putNamedEdge(edgeName, newEdge);
94+
return true;
95+
}
96+
return false;
97+
}
98+
}

src/edu/stanford/nlp/semgraph/semgrex/ssurgeon/Ssurgeon.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,11 @@ public static SsurgeonEdit parseEditLine(String editLine) {
372372
retEdit = AddEdge.createEngAddEdge(argsBox.govNodeName, argsBox.dep, argsBox.reln, argsBox.weight);
373373
} else if (command.equalsIgnoreCase(DeleteGraphFromNode.LABEL)) {
374374
retEdit = new DeleteGraphFromNode(argsBox.node);
375+
} else if (command.equalsIgnoreCase(RelabelNamedEdge.LABEL)) {
376+
// TODO: pass around a Language (perhaps via ssurgeon argument)
377+
// rather than hardcoding English, which is probably not even true
378+
// compared to UniversalEnglish these days
379+
retEdit = RelabelNamedEdge.createEngRelabel(argsBox.edge, argsBox.reln);
375380
} else if (command.equalsIgnoreCase(RemoveEdge.LABEL)) {
376381
GrammaticalRelation reln = null;
377382
if (argsBox.reln != null) {

test/src/edu/stanford/nlp/semgraph/semgrex/ssurgeon/SsurgeonTest.java

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,91 @@ public void readXMLRemoveNamedEdgeIterate() {
178178
assertEquals(newSg, expected);
179179
}
180180

181+
/**
182+
* Test a few various relabel edge operations
183+
*/
184+
@Test
185+
public void readXMLRelabelEdgeIterate() {
186+
String doc = String.join(newline,
187+
"<ssurgeon-pattern-list>",
188+
" <ssurgeon-pattern>",
189+
" <uid>38</uid>",
190+
" <notes>This is a simple test of RelabelNamedEdge</notes>",
191+
" <semgrex>" + XMLUtils.escapeXML("{}=a1 >obj=foo {}=a2") + "</semgrex>",
192+
" <edit-list>relabelNamedEdge -edge foo -reln dep</edit-list>",
193+
" </ssurgeon-pattern>",
194+
"</ssurgeon-pattern-list>");
195+
Ssurgeon inst = Ssurgeon.inst();
196+
List<SsurgeonPattern> patterns = inst.readFromString(doc);
197+
assertEquals(patterns.size(), 1);
198+
SsurgeonPattern pattern = patterns.get(0);
199+
200+
// check a simple case of relabeling
201+
SemanticGraph sg = SemanticGraph.valueOf("[A-0 obj> B-1]");
202+
SemanticGraph expected = SemanticGraph.valueOf("[A-0 dep> B-1]");
203+
SemanticGraph newSg = pattern.iterate(sg);
204+
assertEquals(newSg, expected);
205+
206+
// check iteration over multiple edges
207+
sg = SemanticGraph.valueOf("[A-0 obj> [B-1 obj> C-2]]");
208+
expected = SemanticGraph.valueOf("[A-0 dep> [B-1 dep> C-2]]");
209+
newSg = pattern.iterate(sg);
210+
assertEquals(newSg, expected);
211+
212+
// check that relabeling doesn't change a non-matching edge
213+
// (how would it?)
214+
sg = SemanticGraph.valueOf("[A-0 iobj> B-1]");
215+
expected = SemanticGraph.valueOf("[A-0 iobj> B-1]");
216+
newSg = pattern.iterate(sg);
217+
assertEquals(newSg, expected);
218+
219+
// check that you don't get double edges if an update
220+
// makes two edges into the same edge
221+
sg = SemanticGraph.valueOf("[A-0 obj> B-1 dep> B-1]");
222+
expected = SemanticGraph.valueOf("[A-0 dep> B-1]");
223+
newSg = pattern.iterate(sg);
224+
assertEquals(newSg, expected);
225+
}
226+
227+
228+
/**
229+
* Test that the RelabelNamedEdge operation updates the name of the edge in the SemgrexMatcher
230+
*/
231+
@Test
232+
public void readXMLRelabelEdgeUpdateNamedEdge() {
233+
String doc = String.join(newline,
234+
"<ssurgeon-pattern-list>",
235+
" <ssurgeon-pattern>",
236+
" <uid>38</uid>",
237+
" <notes>This is a simple test of RelabelNamedEdge</notes>",
238+
" <semgrex>" + XMLUtils.escapeXML("{}=a1 >obj=foo {}=a2") + "</semgrex>",
239+
" <edit-list>relabelNamedEdge -edge foo -reln dep</edit-list>",
240+
" <edit-list>relabelNamedEdge -edge foo -reln gov</edit-list>",
241+
" </ssurgeon-pattern>",
242+
"</ssurgeon-pattern-list>");
243+
Ssurgeon inst = Ssurgeon.inst();
244+
List<SsurgeonPattern> patterns = inst.readFromString(doc);
245+
assertEquals(patterns.size(), 1);
246+
SsurgeonPattern pattern = patterns.get(0);
247+
248+
// check the result of a double relabel - should wind up as gov
249+
SemanticGraph sg = SemanticGraph.valueOf("[A-0 obj> B-1]");
250+
SemanticGraph expected = SemanticGraph.valueOf("[A-0 gov> B-1]");
251+
SemanticGraph newSg = pattern.iterate(sg);
252+
assertEquals(newSg, expected);
253+
254+
// in this case, the dep should acquire the name of the obj
255+
// in the SemgrexMatcher. the subsequent operation will pick up
256+
// that edge (with the original edge being deleted) and the
257+
// result will be one edge with the name "gov"
258+
// if the matcher did not update the named edge as expected,
259+
// the second operation would not fire
260+
sg = SemanticGraph.valueOf("[A-0 obj> B-1 dep> B-1]");
261+
expected = SemanticGraph.valueOf("[A-0 gov> B-1]");
262+
newSg = pattern.iterate(sg);
263+
assertEquals(newSg, expected);
264+
}
265+
181266
/**
182267
* Check that cutting a graph with two nodes into two pieces, then
183268
* pruning any disjoint pieces, results in a graph with just the root

0 commit comments

Comments
 (0)