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