public virtual void TestBinarySide() { string[] words = new string[] { "This", "is", "a", "short", "test", "." }; string[] tags = new string[] { "DT", "VBZ", "DT", "JJ", "NN", "." }; NUnit.Framework.Assert.AreEqual(words.Length, tags.Length); IList <TaggedWord> sentence = SentenceUtils.ToTaggedList(Arrays.AsList(words), Arrays.AsList(tags)); State state = ShiftReduceParser.InitialStateFromTaggedSentence(sentence); ShiftTransition shift = new ShiftTransition(); state = shift.Apply(shift.Apply(state)); BinaryTransition transition = new BinaryTransition("NP", BinaryTransition.Side.Right); State next = transition.Apply(state); NUnit.Framework.Assert.AreEqual(BinaryTransition.Side.Right, ShiftReduceUtils.GetBinarySide(next.stack.Peek())); transition = new BinaryTransition("NP", BinaryTransition.Side.Left); next = transition.Apply(state); NUnit.Framework.Assert.AreEqual(BinaryTransition.Side.Left, ShiftReduceUtils.GetBinarySide(next.stack.Peek())); }
/// <summary> /// Shifting is legal as long as the state is not finished and there /// are more items on the queue to be shifted. /// </summary> /// <remarks> /// Shifting is legal as long as the state is not finished and there /// are more items on the queue to be shifted. /// TODO: go through the papers and make sure they don't mention any /// other conditions where one shouldn't shift /// </remarks> public virtual bool IsLegal(State state, IList <ParserConstraint> constraints) { if (state.finished) { return(false); } if (state.tokenPosition >= state.sentence.Count) { return(false); } // We disallow shifting when the previous transition was a right // head transition to a partial (binarized) state // TODO: I don't have an explanation for this, it was just stated // in Zhang & Clark 2009 if (state.stack.Size() > 0) { Tree top = state.stack.Peek(); // Temporary node, eg part of a binarized sequence if (top.Label().Value().StartsWith("@") && top.Children().Length == 2 && ShiftReduceUtils.GetBinarySide(top) == BinaryTransition.Side.Right) { return(false); } } if (constraints == null || state.stack.Size() == 0) { return(true); } Tree top_1 = state.stack.Peek(); // If there are ParserConstraints, you can only shift if shifting // will not make a constraint unsolvable. This happens if we // shift beyond the right end of a constraint which is not solved. foreach (ParserConstraint constraint in constraints) { // either went past or haven't gotten to this constraint yet if (ShiftReduceUtils.RightIndex(top_1) != constraint.end - 1) { continue; } int left = ShiftReduceUtils.LeftIndex(top_1); if (left < constraint.start) { continue; } if (left > constraint.start) { return(false); } if (!ShiftReduceUtils.ConstraintMatchesTreeTop(top_1, constraint)) { return(false); } } return(true); }
/// <summary> /// Returns an attempt at a "gold" transition given the current state /// while parsing a known gold tree. /// </summary> /// <remarks> /// Returns an attempt at a "gold" transition given the current state /// while parsing a known gold tree. /// Tree is passed in by index so the oracle can precompute various /// statistics about the tree. /// If we already finalized, then the correct transition is to idle. /// If the stack is empty, shift is the only possible answer. /// If the first item on the stack is a correct span, correctly /// labeled, and it has unaries transitions above it, then if we are /// not doing compound unaries, the next unary up is the correct /// answer. If we are doing compound unaries, and the state does not /// already have a transition, then the correct answer is a compound /// unary transition to the top of the unary chain. /// If the first item is the entire tree, with no remaining unary /// transitions, then we need to finalize. /// If the first item is a correct span, with or without a correct /// label, and there are no unary transitions to be added, then we /// must look at the next parent. If it has the same left side, then /// we return a shift transition. If it has the same right side, /// then we look at the next subtree on the stack (which must exist). /// If it is also correct, then the transition is to combine the two /// subtrees with the correct label and side. /// TODO: suppose the correct label is not either child label and the /// children are binarized states? We should see what the /// debinarizer does in that case. Perhaps a post-processing step /// If the previous stack item is too small, then any binary reduce /// action is legal, with no gold transition. TODO: can this be improved? /// If the previous stack item is too large, perhaps because of /// incorrectly attached PP/SBAR, for example, we still need to /// binary reduce. TODO: is that correct? TODO: we could look back /// further in the stack to find hints at a label that would work /// better, for example /// If the current item is an incorrect span, then look at the /// containing item. If it has the same left side, shift. If it has /// the same right side, binary reduce (producing an exact span if /// possible). If neither edge is correct, then any of shift or /// binary reduce are acceptable, with no gold transition. TODO: can /// this be improved? /// </remarks> internal virtual OracleTransition GoldTransition(int index, State state) { if (state.finished) { return(new OracleTransition(new IdleTransition(), false, false, false)); } if (state.stack.Size() == 0) { return(new OracleTransition(new ShiftTransition(), false, false, false)); } IDictionary <Tree, Tree> parents = parentMaps[index]; Tree gold = binarizedTrees[index]; IList <Tree> leaves = leafLists[index]; Tree S0 = state.stack.Peek(); Tree enclosingS0 = GetEnclosingTree(S0, parents, leaves); OracleTransition result = GetUnaryTransition(S0, enclosingS0, parents, compoundUnaries); if (result != null) { return(result); } // TODO: we could interject that all trees must end with ROOT, for example if (state.tokenPosition >= state.sentence.Count && state.stack.Size() == 1) { return(new OracleTransition(new FinalizeTransition(rootStates), false, false, false)); } if (state.stack.Size() == 1) { return(new OracleTransition(new ShiftTransition(), false, false, false)); } if (SpansEqual(S0, enclosingS0)) { Tree parent = parents[enclosingS0]; // cannot be root while (SpansEqual(parent, enclosingS0)) { // in case we had missed unary transitions enclosingS0 = parent; parent = parents[parent]; } if (parent.Children()[0] == enclosingS0) { // S0 is the left child of the correct tree return(new OracleTransition(new ShiftTransition(), false, false, false)); } // was the second (right) child. there must be something else on the stack... Tree S1 = state.stack.Pop().Peek(); Tree enclosingS1 = GetEnclosingTree(S1, parents, leaves); if (SpansEqual(S1, enclosingS1)) { // the two subtrees should be combined return(new OracleTransition(new BinaryTransition(parent.Value(), ShiftReduceUtils.GetBinarySide(parent)), false, false, false)); } return(new OracleTransition(null, false, true, false)); } if (ShiftReduceUtils.LeftIndex(S0) == ShiftReduceUtils.LeftIndex(enclosingS0)) { return(new OracleTransition(new ShiftTransition(), false, false, false)); } if (ShiftReduceUtils.RightIndex(S0) == ShiftReduceUtils.RightIndex(enclosingS0)) { Tree S1 = state.stack.Pop().Peek(); Tree enclosingS1 = GetEnclosingTree(S1, parents, leaves); if (enclosingS0 == enclosingS1) { // BinaryTransition with enclosingS0's label, either side, but preferring LEFT return(new OracleTransition(new BinaryTransition(enclosingS0.Value(), BinaryTransition.Side.Left), false, false, true)); } // S1 is smaller than the next tree S0 is supposed to be part of, // so we must have a BinaryTransition if (ShiftReduceUtils.LeftIndex(S1) > ShiftReduceUtils.LeftIndex(enclosingS0)) { return(new OracleTransition(null, false, true, true)); } // S1 is larger than the next tree. This is the worst case return(new OracleTransition(null, true, true, true)); } // S0 doesn't match either endpoint of the enclosing tree return(new OracleTransition(null, true, true, true)); }