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