001    package org.maltparser.parser.guide.decision;
002    
003    import java.lang.reflect.Constructor;
004    import java.lang.reflect.InvocationTargetException;
005    
006    import org.maltparser.core.exception.MaltChainedException;
007    import org.maltparser.core.feature.FeatureModel;
008    import org.maltparser.core.feature.FeatureVector;
009    import org.maltparser.core.syntaxgraph.DependencyStructure;
010    import org.maltparser.parser.DependencyParserConfig;
011    import org.maltparser.parser.guide.ClassifierGuide;
012    import org.maltparser.parser.guide.GuideException;
013    import org.maltparser.parser.guide.instance.AtomicModel;
014    import org.maltparser.parser.guide.instance.FeatureDivideModel;
015    import org.maltparser.parser.guide.instance.InstanceModel;
016    import org.maltparser.parser.history.action.GuideDecision;
017    import org.maltparser.parser.history.action.MultipleDecision;
018    import org.maltparser.parser.history.action.SingleDecision;
019    import org.maltparser.parser.history.container.TableContainer.RelationToNextDecision;
020    /**
021    *
022    * @author Johan Hall
023    * @since 1.1
024    **/
025    public class SeqDecisionModel implements DecisionModel {
026            private ClassifierGuide guide;
027            private String modelName;
028            private FeatureModel featureModel;
029            private InstanceModel instanceModel;
030            private int decisionIndex;
031            private DecisionModel prevDecisionModel;
032            private DecisionModel nextDecisionModel;
033            private String branchedDecisionSymbols;
034            
035            public SeqDecisionModel(ClassifierGuide guide, FeatureModel featureModel) throws MaltChainedException {
036                    this.branchedDecisionSymbols = "";
037                    setGuide(guide);
038                    setFeatureModel(featureModel);
039                    setDecisionIndex(0);
040                    setModelName("sdm"+decisionIndex);
041                    setPrevDecisionModel(null);
042            }
043            
044            public SeqDecisionModel(ClassifierGuide guide, DecisionModel prevDecisionModel, String branchedDecisionSymbol) throws MaltChainedException {
045                    if (branchedDecisionSymbol != null && branchedDecisionSymbol.length() > 0) {
046                            this.branchedDecisionSymbols = branchedDecisionSymbol;
047                    } else {
048                            this.branchedDecisionSymbols = "";
049                    }
050                    setGuide(guide);
051                    setFeatureModel(prevDecisionModel.getFeatureModel());
052                    setDecisionIndex(prevDecisionModel.getDecisionIndex() + 1);
053                    setPrevDecisionModel(prevDecisionModel);
054                    if (branchedDecisionSymbols != null && branchedDecisionSymbols.length() > 0) {
055                            setModelName("sdm"+decisionIndex+branchedDecisionSymbols);
056                    } else {
057                            setModelName("sdm"+decisionIndex);
058                    }
059            }
060            
061            public void updateFeatureModel() throws MaltChainedException {
062                    featureModel.update();
063            }
064            
065            public void finalizeSentence(DependencyStructure dependencyGraph) throws MaltChainedException {
066                    if (instanceModel != null) {
067                            instanceModel.finalizeSentence(dependencyGraph);
068                    }
069                    if (nextDecisionModel != null) {
070                            nextDecisionModel.finalizeSentence(dependencyGraph);
071                    }
072            }
073            
074            public void noMoreInstances() throws MaltChainedException {
075                    if (guide.getGuideMode() == ClassifierGuide.GuideMode.CLASSIFY) {
076                            throw new GuideException("The decision model could not create it's model. ");
077                    }
078                    if (instanceModel != null) {
079                            instanceModel.noMoreInstances();
080                            instanceModel.train();
081                    }
082                    if (nextDecisionModel != null) {
083                            nextDecisionModel.noMoreInstances();
084                    }
085            }
086    
087            public void terminate() throws MaltChainedException {
088                    if (instanceModel != null) {
089                            instanceModel.terminate();
090                            instanceModel = null;
091                    }
092                    if (nextDecisionModel != null) {
093                            nextDecisionModel.terminate();
094                            nextDecisionModel = null;
095                    }
096            }
097            
098            public void addInstance(GuideDecision decision) throws MaltChainedException {
099                    if (decision instanceof SingleDecision) {
100                            throw new GuideException("A sequantial decision model expect a sequence of decisions, not a single decision. ");
101                    }
102                    featureModel.update();
103                    final SingleDecision singleDecision = ((MultipleDecision)decision).getSingleDecision(decisionIndex);
104                    if (instanceModel == null) {
105                            initInstanceModel(singleDecision.getTableContainer().getTableContainerName());
106                    }
107                    instanceModel.addInstance(singleDecision);
108                    if (singleDecision.continueWithNextDecision() && decisionIndex+1 < decision.numberOfDecisions()) {
109                            if (nextDecisionModel == null) {
110                                    initNextDecisionModel(((MultipleDecision)decision).getSingleDecision(decisionIndex+1), branchedDecisionSymbols);
111                            }
112                            nextDecisionModel.addInstance(decision);
113                    }
114            }
115            
116            public boolean predict(GuideDecision decision) throws MaltChainedException {
117                    if (decision instanceof SingleDecision) {
118                            throw new GuideException("A sequantial decision model expect a sequence of decisions, not a single decision. ");
119                    }
120                    featureModel.update();
121                    final SingleDecision singleDecision = ((MultipleDecision)decision).getSingleDecision(decisionIndex);
122                    if (instanceModel == null) {
123                            initInstanceModel(singleDecision.getTableContainer().getTableContainerName());
124                    }
125    
126                    boolean success = instanceModel.predict(singleDecision);
127                    if (singleDecision.continueWithNextDecision() && decisionIndex+1 < decision.numberOfDecisions()) {
128                            if (nextDecisionModel == null) {
129                                    initNextDecisionModel(((MultipleDecision)decision).getSingleDecision(decisionIndex+1), branchedDecisionSymbols);
130                            }
131                            success = nextDecisionModel.predict(decision) && success;
132                    }
133                    return success;
134            }
135            
136            public FeatureVector predictExtract(GuideDecision decision) throws MaltChainedException {
137                    if (decision instanceof SingleDecision) {
138                            throw new GuideException("A sequantial decision model expect a sequence of decisions, not a single decision. ");
139                    }
140                    featureModel.update();
141                    final SingleDecision singleDecision = ((MultipleDecision)decision).getSingleDecision(decisionIndex);
142                    if (instanceModel == null) {
143                            initInstanceModel(singleDecision.getTableContainer().getTableContainerName());
144                    }
145    
146                    FeatureVector fv = instanceModel.predictExtract(singleDecision);
147                    if (singleDecision.continueWithNextDecision() && decisionIndex+1 < decision.numberOfDecisions()) {
148                            if (nextDecisionModel == null) {
149                                    initNextDecisionModel(((MultipleDecision)decision).getSingleDecision(decisionIndex+1), branchedDecisionSymbols);
150                            }
151                            nextDecisionModel.predictExtract(decision);
152                    }
153                    return fv;
154            }
155            
156            public FeatureVector extract() throws MaltChainedException {
157                    featureModel.update();
158                    return instanceModel.extract(); // TODO handle many feature vectors
159            }
160            
161            public boolean predictFromKBestList(GuideDecision decision) throws MaltChainedException {
162                    if (decision instanceof SingleDecision) {
163                            throw new GuideException("A sequantial decision model expect a sequence of decisions, not a single decision. ");
164                    }
165                    
166                    boolean success = false;
167                    final SingleDecision singleDecision = ((MultipleDecision)decision).getSingleDecision(decisionIndex);
168                    // TODO develop different strategies for resolving which kBestlist that should be used
169                    if (nextDecisionModel != null && singleDecision.continueWithNextDecision()) {
170                            success = nextDecisionModel.predictFromKBestList(decision);
171                    }
172                    if (!success) {
173                            success = singleDecision.updateFromKBestList();
174                            if (success && singleDecision.continueWithNextDecision() && decisionIndex+1 < decision.numberOfDecisions()) {
175                                    if (nextDecisionModel == null) {
176                                            initNextDecisionModel(((MultipleDecision)decision).getSingleDecision(decisionIndex+1), branchedDecisionSymbols);
177                                    }
178                                    nextDecisionModel.predict(decision);
179                            }
180                    }
181                    return success;
182            }
183            
184    
185            public ClassifierGuide getGuide() {
186                    return guide;
187            }
188    
189            public String getModelName() {
190                    return modelName;
191            }
192            
193            public FeatureModel getFeatureModel() {
194                    return featureModel;
195            }
196    
197            public int getDecisionIndex() {
198                    return decisionIndex;
199            }
200    
201            public DecisionModel getPrevDecisionModel() {
202                    return prevDecisionModel;
203            }
204    
205            public DecisionModel getNextDecisionModel() {
206                    return nextDecisionModel;
207            }
208            
209            private void setPrevDecisionModel(DecisionModel prevDecisionModel) {
210                    this.prevDecisionModel = prevDecisionModel;
211            }
212            
213            private void setNextDecisionModel(DecisionModel nextDecisionModel) {
214                    this.nextDecisionModel = nextDecisionModel;
215            }
216    
217            private void setFeatureModel(FeatureModel featureModel) {
218                    this.featureModel = featureModel;
219            }
220            
221            private void setDecisionIndex(int decisionIndex) {
222                    this.decisionIndex = decisionIndex;
223            }
224    
225            private void setModelName(String modelName) {
226                    this.modelName = modelName;
227            }
228            
229            private void setGuide(ClassifierGuide guide) {
230                    this.guide = guide;
231            }
232            
233            private void initInstanceModel(String subModelName) throws MaltChainedException {
234                    FeatureVector fv = featureModel.getFeatureVector(branchedDecisionSymbols+"."+subModelName);
235                    if (fv == null) {
236                            fv = featureModel.getFeatureVector(subModelName);
237                    }
238                    if (fv == null) {
239                            fv = featureModel.getMainFeatureVector();
240                    }
241                    
242                    DependencyParserConfig c = guide.getConfiguration();
243                    
244                    if (c.getOptionValue("guide", "data_split_column").toString().length() == 0) {
245                            instanceModel = new AtomicModel(-1, fv, this);
246                    } else {
247                            instanceModel = new FeatureDivideModel(fv, this);
248                    }
249            }
250            
251            private void initNextDecisionModel(SingleDecision decision, String branchedDecisionSymbol) throws MaltChainedException {
252                    Class<?> decisionModelClass = null;
253                    if (decision.getRelationToNextDecision() == RelationToNextDecision.SEQUANTIAL) {
254                            decisionModelClass = org.maltparser.parser.guide.decision.SeqDecisionModel.class;
255                    } else if (decision.getRelationToNextDecision() == RelationToNextDecision.BRANCHED) {
256                            decisionModelClass = org.maltparser.parser.guide.decision.BranchedDecisionModel.class;
257                    } else if (decision.getRelationToNextDecision() == RelationToNextDecision.NONE) {
258                            decisionModelClass = org.maltparser.parser.guide.decision.OneDecisionModel.class;
259                    }
260    
261                    if (decisionModelClass == null) {
262                            throw new GuideException("Could not find an appropriate decision model for the relation to the next decision"); 
263                    }
264                    
265                    try {
266                            Class<?>[] argTypes = { org.maltparser.parser.guide.ClassifierGuide.class, org.maltparser.parser.guide.decision.DecisionModel.class, 
267                                                                            java.lang.String.class };
268                            Object[] arguments = new Object[3];
269                            arguments[0] = getGuide();
270                            arguments[1] = this;
271                            arguments[2] = branchedDecisionSymbol;
272                            Constructor<?> constructor = decisionModelClass.getConstructor(argTypes);
273                            setNextDecisionModel((DecisionModel)constructor.newInstance(arguments));
274                    } catch (NoSuchMethodException e) {
275                            throw new GuideException("The decision model class '"+decisionModelClass.getName()+"' cannot be initialized. ", e);
276                    } catch (InstantiationException e) {
277                            throw new GuideException("The decision model class '"+decisionModelClass.getName()+"' cannot be initialized. ", e);
278                    } catch (IllegalAccessException e) {
279                            throw new GuideException("The decision model class '"+decisionModelClass.getName()+"' cannot be initialized. ", e);
280                    } catch (InvocationTargetException e) {
281                            throw new GuideException("The decision model class '"+decisionModelClass.getName()+"' cannot be initialized. ", e);
282                    }
283            }
284            
285            public String toString() {
286                    final StringBuilder sb = new StringBuilder();
287                    sb.append(modelName + ", ");
288                    sb.append(nextDecisionModel.toString());
289                    return sb.toString();
290            }
291    }