edu.stanford.nlp.parser.shiftreduce.BinaryTransition Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of stanford-parser Show documentation
Show all versions of stanford-parser Show documentation
Stanford Parser processes raw text in English, Chinese, German, Arabic, and French, and extracts constituency parse trees.
package edu.stanford.nlp.parser.shiftreduce;
import java.util.List;
import edu.stanford.nlp.ling.CoreLabel;
import edu.stanford.nlp.parser.common.ParserConstraint;
import edu.stanford.nlp.trees.LabeledScoredTreeNode;
import edu.stanford.nlp.trees.Tree;
import edu.stanford.nlp.trees.TreeCoreAnnotations;
import edu.stanford.nlp.util.TreeShapedStack;
/**
* Transition that makes a binary parse node in a partially finished tree.
*/
public class BinaryTransition implements Transition {
public final String label;
/** Which side the head is on */
public final Side side;
public enum Side {
LEFT, RIGHT
}
public BinaryTransition(String label, Side side) {
this.label = label;
this.side = side;
}
/**
* Legal as long as there are at least two items on the state's stack.
*/
public boolean isLegal(State state, List constraints) {
// some of these quotes come directly from Zhang Clark 09
if (state.finished) {
return false;
}
if (state.stack.size() <= 1) {
return false;
}
// at least one of the two nodes on top of stack must be non-temporary
if (ShiftReduceUtils.isTemporary(state.stack.peek()) && ShiftReduceUtils.isTemporary(state.stack.pop().peek())) {
return false;
}
if (ShiftReduceUtils.isTemporary(state.stack.peek())) {
if (side == Side.LEFT) {
return false;
}
if (!ShiftReduceUtils.isEquivalentCategory(label, state.stack.peek().value())) {
return false;
}
}
if (ShiftReduceUtils.isTemporary(state.stack.pop().peek())) {
if (side == Side.RIGHT) {
return false;
}
if (!ShiftReduceUtils.isEquivalentCategory(label, state.stack.pop().peek().value())) {
return false;
}
}
// don't allow binarized labels if it makes the state have a stack
// of size 1 and a queue of size 0
if (state.stack.size() == 2 && isBinarized() && state.endOfQueue()) {
return false;
}
// when the stack contains only two nodes, temporary resulting
// nodes from binary reduce must be left-headed
if (state.stack.size() == 2 && isBinarized() && side == Side.RIGHT) {
return false;
}
// when the queue is empty and the stack contains more than two
// nodes, with the third node from the top being temporary, binary
// reduce can be applied only if the resulting node is non-temporary
if (state.endOfQueue() && state.stack.size() > 2 && ShiftReduceUtils.isTemporary(state.stack.pop().pop().peek()) && isBinarized()) {
return false;
}
// when the stack contains more than two nodes, with the third
// node from the top being temporary, temporary resulting nodes
// from binary reduce must be left-headed
if (state.stack.size() > 2 && ShiftReduceUtils.isTemporary(state.stack.pop().pop().peek()) && isBinarized() && side == Side.RIGHT) {
return false;
}
if (constraints == null) {
return true;
}
final Tree top = state.stack.peek();
final int leftTop = ShiftReduceUtils.leftIndex(top);
final int rightTop = ShiftReduceUtils.rightIndex(top);
final Tree next = state.stack.pop().peek();
final int leftNext = ShiftReduceUtils.leftIndex(next);
// The binary transitions are affected by constraints in the
// following two circumstances. If a transition would cross the
// left boundary of a constraint, that is illegal. If the
// transition is exactly the right size for the constraint and
// would make a temporary node, that is also illegal.
for (ParserConstraint constraint : constraints) {
if (leftTop == constraint.start) {
// can't binary reduce away from a tree which doesn't match a constraint
if (rightTop == constraint.end - 1) {
if (!ShiftReduceUtils.constraintMatchesTreeTop(top, constraint)) {
return false;
} else {
continue;
}
} else if (rightTop >= constraint.end) {
continue;
} else {
// can't binary reduce if it would make the tree cross the left boundary
return false;
}
}
// top element is further left than the constraint, so
// there's no harm to be done by binary reduce
if (leftTop < constraint.start) {
continue;
}
// top element is past the end of the constraint, so it must already be satisfied
if (leftTop >= constraint.end) {
continue;
}
// now leftTop > constraint.start and < constraint.end, eg inside the constraint
// the next case is no good because it crosses the boundary
if (leftNext < constraint.start) {
return false;
}
if (leftNext > constraint.start) {
continue;
}
// can't transition to a binarized node when there's a constraint that matches.
if (rightTop == constraint.end - 1 && isBinarized()) {
return false;
}
}
return true;
}
public boolean isBinarized() {
return (label.charAt(0) == '@');
}
/**
* Add a binary node to the existing node on top of the stack
*/
public State apply(State state) {
return apply(state, 0.0);
}
/**
* Add a binary node to the existing node on top of the stack
*/
public State apply(State state, double scoreDelta) {
TreeShapedStack stack = state.stack;
Tree right = stack.peek();
stack = stack.pop();
Tree left = stack.peek();
stack = stack.pop();
Tree head;
switch(side) {
case LEFT:
head = left;
break;
case RIGHT:
head = right;
break;
default:
throw new IllegalArgumentException("Unknown side " + side);
}
if (!(head.label() instanceof CoreLabel)) {
throw new IllegalArgumentException("Stack should have CoreLabel nodes");
}
CoreLabel headLabel = (CoreLabel) head.label();
CoreLabel production = new CoreLabel();
production.setValue(label);
production.set(TreeCoreAnnotations.HeadWordLabelAnnotation.class, headLabel.get(TreeCoreAnnotations.HeadWordLabelAnnotation.class));
production.set(TreeCoreAnnotations.HeadTagLabelAnnotation.class, headLabel.get(TreeCoreAnnotations.HeadTagLabelAnnotation.class));
Tree newTop = new LabeledScoredTreeNode(production);
newTop.addChild(left);
newTop.addChild(right);
stack = stack.push(newTop);
return new State(stack, state.transitions.push(this), state.separators, state.sentence, state.tokenPosition, state.score + scoreDelta, false);
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof BinaryTransition)) {
return false;
}
String otherLabel = ((BinaryTransition) o).label;
Side otherSide = ((BinaryTransition) o).side;
return otherSide.equals(side) && label.equals(otherLabel);
}
@Override
public int hashCode() {
// TODO: fix the hashcode for the side? would require rebuilding all models
switch(side) {
case LEFT:
return 97197711 ^ label.hashCode();
case RIGHT:
return 97197711 ^ label.hashCode();
default:
throw new IllegalArgumentException("Unknown side " + side);
}
}
@Override
public String toString() {
switch(side) {
case LEFT:
return "LeftBinary(" + label + ")";
case RIGHT:
return "RightBinary(" + label + ")";
default:
throw new IllegalArgumentException("Unknown side " + side);
}
}
private static final long serialVersionUID = 1;
}