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 updateCardinality() throws MaltChainedException {
066    //              featureModel.updateCardinality();
067    //      }
068            
069            public void finalizeSentence(DependencyStructure dependencyGraph) throws MaltChainedException {
070                    if (instanceModel != null) {
071                            instanceModel.finalizeSentence(dependencyGraph);
072                    }
073                    if (nextDecisionModel != null) {
074                            nextDecisionModel.finalizeSentence(dependencyGraph);
075                    }
076            }
077            
078            public void noMoreInstances() throws MaltChainedException {
079                    if (guide.getGuideMode() == ClassifierGuide.GuideMode.CLASSIFY) {
080                            throw new GuideException("The decision model could not create it's model. ");
081                    }
082    //              featureModel.updateCardinality();
083                    if (instanceModel != null) {
084                            instanceModel.noMoreInstances();
085                            instanceModel.train();
086                    }
087                    if (nextDecisionModel != null) {
088                            nextDecisionModel.noMoreInstances();
089                    }
090            }
091    
092            public void terminate() throws MaltChainedException {
093                    if (instanceModel != null) {
094                            instanceModel.terminate();
095                            instanceModel = null;
096                    }
097                    if (nextDecisionModel != null) {
098                            nextDecisionModel.terminate();
099                            nextDecisionModel = null;
100                    }
101            }
102            
103            public void addInstance(GuideDecision decision) throws MaltChainedException {
104                    if (decision instanceof SingleDecision) {
105                            throw new GuideException("A sequantial decision model expect a sequence of decisions, not a single decision. ");
106                    }
107                    updateFeatureModel();
108                    final SingleDecision singleDecision = ((MultipleDecision)decision).getSingleDecision(decisionIndex);
109                    if (instanceModel == null) {
110                            initInstanceModel(singleDecision.getTableContainer().getTableContainerName());
111                    }
112                    instanceModel.addInstance(singleDecision);
113                    if (singleDecision.continueWithNextDecision() && decisionIndex+1 < decision.numberOfDecisions()) {
114                            if (nextDecisionModel == null) {
115                                    initNextDecisionModel(((MultipleDecision)decision).getSingleDecision(decisionIndex+1), branchedDecisionSymbols);
116                            }
117                            nextDecisionModel.addInstance(decision);
118                    }
119            }
120            
121            public boolean predict(GuideDecision decision) throws MaltChainedException {
122                    if (decision instanceof SingleDecision) {
123                            throw new GuideException("A sequantial decision model expect a sequence of decisions, not a single decision. ");
124                    }
125                    updateFeatureModel();
126                    final SingleDecision singleDecision = ((MultipleDecision)decision).getSingleDecision(decisionIndex);
127                    if (instanceModel == null) {
128                            initInstanceModel(singleDecision.getTableContainer().getTableContainerName());
129                    }
130    
131                    boolean success = instanceModel.predict(singleDecision);
132                    if (singleDecision.continueWithNextDecision() && decisionIndex+1 < decision.numberOfDecisions()) {
133                            if (nextDecisionModel == null) {
134                                    initNextDecisionModel(((MultipleDecision)decision).getSingleDecision(decisionIndex+1), branchedDecisionSymbols);
135                            }
136                            success = nextDecisionModel.predict(decision) && success;
137                    }
138                    return success;
139            }
140            
141            public FeatureVector predictExtract(GuideDecision decision) throws MaltChainedException {
142                    if (decision instanceof SingleDecision) {
143                            throw new GuideException("A sequantial decision model expect a sequence of decisions, not a single decision. ");
144                    }
145                    updateFeatureModel();
146                    final SingleDecision singleDecision = ((MultipleDecision)decision).getSingleDecision(decisionIndex);
147                    if (instanceModel == null) {
148                            initInstanceModel(singleDecision.getTableContainer().getTableContainerName());
149                    }
150    
151                    FeatureVector fv = instanceModel.predictExtract(singleDecision);
152                    if (singleDecision.continueWithNextDecision() && decisionIndex+1 < decision.numberOfDecisions()) {
153                            if (nextDecisionModel == null) {
154                                    initNextDecisionModel(((MultipleDecision)decision).getSingleDecision(decisionIndex+1), branchedDecisionSymbols);
155                            }
156                            nextDecisionModel.predictExtract(decision);
157                    }
158                    return fv;
159            }
160            
161            public FeatureVector extract() throws MaltChainedException {
162                    updateFeatureModel();
163                    return instanceModel.extract(); // TODO handle many feature vectors
164            }
165            
166            public boolean predictFromKBestList(GuideDecision decision) throws MaltChainedException {
167                    if (decision instanceof SingleDecision) {
168                            throw new GuideException("A sequantial decision model expect a sequence of decisions, not a single decision. ");
169                    }
170                    
171                    boolean success = false;
172                    final SingleDecision singleDecision = ((MultipleDecision)decision).getSingleDecision(decisionIndex);
173                    // TODO develop different strategies for resolving which kBestlist that should be used
174                    if (nextDecisionModel != null && singleDecision.continueWithNextDecision()) {
175                            success = nextDecisionModel.predictFromKBestList(decision);
176                    }
177                    if (!success) {
178                            success = singleDecision.updateFromKBestList();
179                            if (success && singleDecision.continueWithNextDecision() && decisionIndex+1 < decision.numberOfDecisions()) {
180                                    if (nextDecisionModel == null) {
181                                            initNextDecisionModel(((MultipleDecision)decision).getSingleDecision(decisionIndex+1), branchedDecisionSymbols);
182                                    }
183                                    nextDecisionModel.predict(decision);
184                            }
185                    }
186                    return success;
187            }
188            
189    
190            public ClassifierGuide getGuide() {
191                    return guide;
192            }
193    
194            public String getModelName() {
195                    return modelName;
196            }
197            
198            public FeatureModel getFeatureModel() {
199                    return featureModel;
200            }
201    
202            public int getDecisionIndex() {
203                    return decisionIndex;
204            }
205    
206            public DecisionModel getPrevDecisionModel() {
207                    return prevDecisionModel;
208            }
209    
210            public DecisionModel getNextDecisionModel() {
211                    return nextDecisionModel;
212            }
213            
214            private void setPrevDecisionModel(DecisionModel prevDecisionModel) {
215                    this.prevDecisionModel = prevDecisionModel;
216            }
217            
218            private void setNextDecisionModel(DecisionModel nextDecisionModel) {
219                    this.nextDecisionModel = nextDecisionModel;
220            }
221    
222            private void setFeatureModel(FeatureModel featureModel) {
223                    this.featureModel = featureModel;
224            }
225            
226            private void setDecisionIndex(int decisionIndex) {
227                    this.decisionIndex = decisionIndex;
228            }
229    
230            private void setModelName(String modelName) {
231                    this.modelName = modelName;
232            }
233            
234            private void setGuide(ClassifierGuide guide) {
235                    this.guide = guide;
236            }
237            
238            private void initInstanceModel(String subModelName) throws MaltChainedException {
239                    FeatureVector fv = featureModel.getFeatureVector(branchedDecisionSymbols+"."+subModelName);
240                    if (fv == null) {
241                            fv = featureModel.getFeatureVector(subModelName);
242                    }
243                    if (fv == null) {
244                            fv = featureModel.getMainFeatureVector();
245                    }
246                    
247                    DependencyParserConfig c = guide.getConfiguration();
248                    
249    //              if (c.getOptionValue("guide", "tree_automatic_split_order").toString().equals("yes") ||
250    //                              (c.getOptionValue("guide", "tree_split_columns")!=null &&
251    //                      c.getOptionValue("guide", "tree_split_columns").toString().length() > 0) ||
252    //                      (c.getOptionValue("guide", "tree_split_structures")!=null &&
253    //                      c.getOptionValue("guide", "tree_split_structures").toString().length() > 0)) {
254    //                      instanceModel = new DecisionTreeModel(fv, this); 
255    //              }else 
256                    if (c.getOptionValue("guide", "data_split_column").toString().length() == 0) {
257                            instanceModel = new AtomicModel(-1, fv, this);
258                    } else {
259                            instanceModel = new FeatureDivideModel(fv, this);
260                    }
261            }
262            
263            private void initNextDecisionModel(SingleDecision decision, String branchedDecisionSymbol) throws MaltChainedException {
264                    Class<?> decisionModelClass = null;
265                    if (decision.getRelationToNextDecision() == RelationToNextDecision.SEQUANTIAL) {
266                            decisionModelClass = org.maltparser.parser.guide.decision.SeqDecisionModel.class;
267                    } else if (decision.getRelationToNextDecision() == RelationToNextDecision.BRANCHED) {
268                            decisionModelClass = org.maltparser.parser.guide.decision.BranchedDecisionModel.class;
269                    } else if (decision.getRelationToNextDecision() == RelationToNextDecision.NONE) {
270                            decisionModelClass = org.maltparser.parser.guide.decision.OneDecisionModel.class;
271                    }
272    
273                    if (decisionModelClass == null) {
274                            throw new GuideException("Could not find an appropriate decision model for the relation to the next decision"); 
275                    }
276                    
277                    try {
278                            Class<?>[] argTypes = { org.maltparser.parser.guide.ClassifierGuide.class, org.maltparser.parser.guide.decision.DecisionModel.class, 
279                                                                            java.lang.String.class };
280                            Object[] arguments = new Object[3];
281                            arguments[0] = getGuide();
282                            arguments[1] = this;
283                            arguments[2] = branchedDecisionSymbol;
284                            Constructor<?> constructor = decisionModelClass.getConstructor(argTypes);
285                            setNextDecisionModel((DecisionModel)constructor.newInstance(arguments));
286                    } catch (NoSuchMethodException e) {
287                            throw new GuideException("The decision model class '"+decisionModelClass.getName()+"' cannot be initialized. ", e);
288                    } catch (InstantiationException e) {
289                            throw new GuideException("The decision model class '"+decisionModelClass.getName()+"' cannot be initialized. ", e);
290                    } catch (IllegalAccessException e) {
291                            throw new GuideException("The decision model class '"+decisionModelClass.getName()+"' cannot be initialized. ", e);
292                    } catch (InvocationTargetException e) {
293                            throw new GuideException("The decision model class '"+decisionModelClass.getName()+"' cannot be initialized. ", e);
294                    }
295            }
296            
297            public String toString() {
298                    final StringBuilder sb = new StringBuilder();
299                    sb.append(modelName + ", ");
300                    sb.append(nextDecisionModel.toString());
301                    return sb.toString();
302            }
303    }