001package org.maltparser.parser.algorithm.twoplanar; 002 003import java.util.Stack; 004 005import org.maltparser.core.exception.MaltChainedException; 006import org.maltparser.core.propagation.PropagationManager; 007import org.maltparser.core.syntaxgraph.DependencyStructure; 008import org.maltparser.core.syntaxgraph.edge.Edge; 009import org.maltparser.core.syntaxgraph.node.DependencyNode; 010import org.maltparser.parser.ParserConfiguration; 011import org.maltparser.parser.TransitionSystem; 012import org.maltparser.parser.history.GuideUserHistory; 013import org.maltparser.parser.history.action.ComplexDecisionAction; 014import org.maltparser.parser.history.action.GuideUserAction; 015import org.maltparser.parser.transition.TransitionTable; 016/** 017 * @author Carlos Gomez Rodriguez 018 * 019 */ 020public class TwoPlanar extends TransitionSystem { 021 protected static final int SHIFT = 1; 022 protected static final int SWITCH = 2; 023 protected static final int RIGHTARC = 3; 024 protected static final int LEFTARC = 4; 025 protected static final int REDUCE = 5; 026 protected static final int REDUCEBOTH = 6; 027 028 public TwoPlanar(PropagationManager propagationManager) throws MaltChainedException { 029 super(propagationManager); 030 } 031 032 public void apply(GuideUserAction currentAction, ParserConfiguration config) throws MaltChainedException { 033 TwoPlanarConfig planarConfig = (TwoPlanarConfig)config; 034 Stack<DependencyNode> activeStack = planarConfig.getActiveStack(); 035 Stack<DependencyNode> inactiveStack = planarConfig.getInactiveStack(); 036 Stack<DependencyNode> input = planarConfig.getInput(); 037 currentAction.getAction(actionContainers); 038 Edge e = null; 039 int actionCode = transActionContainer.getActionCode(); 040 switch ( actionCode ) { 041 case LEFTARC: 042 e = planarConfig.getDependencyStructure().addDependencyEdge(input.peek().getIndex(), activeStack.peek().getIndex()); 043 addEdgeLabels(e); 044 break; 045 case RIGHTARC: 046 e = planarConfig.getDependencyStructure().addDependencyEdge(activeStack.peek().getIndex(), input.peek().getIndex()); 047 addEdgeLabels(e); 048 break; 049 case SWITCH: 050 planarConfig.switchStacks(); 051 if ( planarConfig.reduceAfterSwitch() ) 052 { 053 planarConfig.getActiveStack().pop(); 054 } 055 break; 056 case REDUCE: 057 activeStack.pop(); 058 break; 059 case REDUCEBOTH: 060 activeStack.pop(); 061 inactiveStack.pop(); 062 break; 063 default: //SHIFT 064 DependencyNode n = input.pop(); 065 activeStack.push(n); 066 inactiveStack.push(n); 067 break; 068 } 069 planarConfig.setLastAction(actionCode); 070 } 071 072 073 public GuideUserAction getDeterministicAction(GuideUserHistory history, ParserConfiguration config) throws MaltChainedException { 074 TwoPlanarConfig theConfig = (TwoPlanarConfig)config; 075 if (theConfig.getRootHandling() != TwoPlanarConfig.NORMAL && theConfig.getActiveStack().peek().isRoot()) { 076 return updateActionContainers(history, TwoPlanar.SHIFT, null); 077 } 078 return null; 079 } 080 081 protected void addAvailableTransitionToTable(TransitionTable ttable) throws MaltChainedException { 082 ttable.addTransition(SHIFT, "SH", false, null); 083 ttable.addTransition(SWITCH, "SW", false, null); 084 ttable.addTransition(REDUCE, "RE", false, null); 085 ttable.addTransition(REDUCEBOTH, "RB", false, null); 086 ttable.addTransition(RIGHTARC, "RA", true, null); 087 ttable.addTransition(LEFTARC, "LA", true, null); 088 } 089 090 protected void initWithDefaultTransitions(GuideUserHistory history) throws MaltChainedException { 091 GuideUserAction currentAction = new ComplexDecisionAction(history); 092 093 transActionContainer.setAction(SHIFT); 094 transActionContainer.setAction(REDUCE); 095 transActionContainer.setAction(SWITCH); //TODO it seems like a good idea to do this, but I don't know what it actually does 096 transActionContainer.setAction(REDUCEBOTH); //TODO same as above 097 for (int i = 0; i < arcLabelActionContainers.length; i++) { 098 arcLabelActionContainers[i].setAction(-1); 099 } 100 currentAction.addAction(actionContainers); 101 } 102 103 public String getName() { 104 return "two-planar arc-eager"; 105 } 106 107 public boolean permissible(GuideUserAction currentAction, ParserConfiguration config) throws MaltChainedException { 108 currentAction.getAction(actionContainers); 109 int trans = transActionContainer.getActionCode(); 110 TwoPlanarConfig planarConfig = (TwoPlanarConfig)config; 111 DependencyNode activeStackPeek = planarConfig.getActiveStack().peek(); 112 DependencyNode inactiveStackPeek = planarConfig.getInactiveStack().peek(); 113 DependencyNode inputPeek = planarConfig.getInput().peek(); 114 DependencyStructure dg = planarConfig.getDependencyGraph(); 115 //int rootHandling = planarConfig.getRootHandling(); 116 boolean singleHeadConstraint = planarConfig.requiresSingleHead(); 117 boolean noCoveredRootsConstraint = planarConfig.requiresNoCoveredRoots(); 118 boolean acyclicityConstraint = planarConfig.requiresAcyclicity(); 119 //boolean connectednessConstraintOnReduce = planarConfig.requiresConnectednessCheckOnReduce(); 120 //boolean connectednessConstraintOnShift = planarConfig.requiresConnectednessCheckOnShift(); 121 if ((trans == LEFTARC || trans == RIGHTARC) && !isActionContainersLabeled()) { 122 return false; 123 } 124 //if ((trans == LEFTARC || trans == REDUCE) && stackPeek.isRoot()) { 125 // return false; 126 //} 127 if (trans == LEFTARC) { 128 //avoid making root child of something 129 if ( activeStackPeek.isRoot() ) 130 return false; 131 //enforce single-head constraint if present 132 if ( activeStackPeek.hasHead() && singleHeadConstraint ) 133 return false; 134 //avoid two links being created from and to the same node 135 if ( activeStackPeek.hasHead() && dg.getTokenNode(activeStackPeek.getIndex()).getHead().getIndex() == inputPeek.getIndex() ) 136 return false; 137 //enforce acyclicity constraint if present 138 if ( acyclicityConstraint && activeStackPeek.findComponent().getIndex() == inputPeek.findComponent().getIndex() ) 139 return false; 140 } 141 if (trans == RIGHTARC) { 142 //enforce single-head constraint if present 143 if ( inputPeek.hasHead() && singleHeadConstraint ) 144 return false; 145 //avoid two links being created from and to the same node 146 if ( inputPeek.hasHead() && dg.getTokenNode(inputPeek.getIndex()).getHead().getIndex() == activeStackPeek.getIndex() ) 147 return false; 148 //enforce acyclicity constraint if present 149 if ( acyclicityConstraint && activeStackPeek.findComponent().getIndex() == inputPeek.findComponent().getIndex() ) 150 return false; 151 } 152 if (trans == REDUCE) { 153 //do not reduce the dummy root 154 if ( activeStackPeek.isRoot() ) 155 return false; 156 //enforce no-covered-roots constraint if present 157 if ( !activeStackPeek.hasHead() && noCoveredRootsConstraint ) 158 return false; 159 //TODO does this line still make sense? (from Nivre arc-eager) 160 //if ( !stackPeek.hasHead() && rootHandling == PlanarConfig.STRICT ) 161 // return false; 162 //enforce connectedness constraint if present 163 /* 164 if ( connectednessConstraintOnReduce ) 165 { 166 boolean path1 = ( stackPeek.findComponent().getIndex() == inputPeek.findComponent().getIndex() ); 167 boolean path2; 168 if ( planarConfig.getStack().size() < 2 ) path2=false; 169 else 170 { 171 DependencyNode stackPrev = planarConfig.getStack().get(planarConfig.getStack().size()-2); 172 path2 = stackPrev.findComponent().getIndex() == stackPeek.findComponent().getIndex(); 173 } 174 return path1 || path2; 175 } 176 */ 177 } 178 if ( trans == SHIFT ) 179 { 180 /* 181 if ( connectednessConstraintOnShift && planarConfig.getInput().size() == 1 ) //last word 182 { 183 boolean path = ( planarConfig.getDependencyGraph().getTokenNode(1).findComponent().getIndex() == inputPeek.findComponent().getIndex() ); //require connection to 1st 184 return path; 185 } 186 */ 187 } 188 if (trans == REDUCEBOTH) { 189 //do not reduce the dummy root 190 if ( activeStackPeek.isRoot() || inactiveStackPeek.isRoot() ) 191 return false; 192 //enforce no-covered-roots constraint if present 193 if ( (!activeStackPeek.hasHead() || inactiveStackPeek.hasHead()) && noCoveredRootsConstraint ) 194 return false; 195 196 //TODO remove this: 197 //not using this transition at the moment, so 198 return false; 199 } 200 if ( trans == SWITCH ) 201 { 202 if ( planarConfig.reduceAfterSwitch() ) 203 { 204 if ( inactiveStackPeek.isRoot() ) 205 return false; 206 //enforce no-covered-roots constraint if present 207 if ( !inactiveStackPeek.hasHead() && noCoveredRootsConstraint ) 208 return false; 209 } 210 else 211 { 212 if ( planarConfig.getLastAction() == SWITCH ) return false; 213 } 214 } 215 return true; 216 } 217 218 public GuideUserAction defaultAction(GuideUserHistory history, ParserConfiguration configuration) throws MaltChainedException { 219 return updateActionContainers(history, TwoPlanar.SHIFT, null); 220 } 221 222 223}