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