001package org.maltparser.parser.guide.decision;
002
003import org.maltparser.core.exception.MaltChainedException;
004import org.maltparser.core.feature.FeatureModel;
005import org.maltparser.core.feature.FeatureVector;
006import org.maltparser.core.helper.HashMap;
007import org.maltparser.core.syntaxgraph.DependencyStructure;
008import org.maltparser.parser.guide.ClassifierGuide;
009import org.maltparser.parser.guide.GuideException;
010import org.maltparser.parser.guide.instance.AtomicModel;
011import org.maltparser.parser.guide.instance.FeatureDivideModel;
012import org.maltparser.parser.guide.instance.InstanceModel;
013import org.maltparser.parser.history.action.GuideDecision;
014import org.maltparser.parser.history.action.MultipleDecision;
015import org.maltparser.parser.history.action.SingleDecision;
016import org.maltparser.parser.history.container.TableContainer.RelationToNextDecision;
017/**
018*
019* @author Johan Hall
020**/
021public class BranchedDecisionModel implements DecisionModel {
022        private final ClassifierGuide guide;
023        private final String modelName;
024//      private final FeatureModel featureModel;
025        private InstanceModel instanceModel;
026        private final int decisionIndex;
027        private final DecisionModel parentDecisionModel;
028        private final HashMap<Integer,DecisionModel> children;
029        private final String branchedDecisionSymbols;
030        
031        public BranchedDecisionModel(ClassifierGuide _guide) throws MaltChainedException {
032                this.guide = _guide;
033                this.branchedDecisionSymbols = "";
034//              this.featureModel = _featureModel;
035                this.decisionIndex = 0;
036                this.modelName = "bdm0";
037                this.parentDecisionModel = null;
038                this.children = new HashMap<Integer,DecisionModel>();
039        }
040        
041        public BranchedDecisionModel(ClassifierGuide _guide, DecisionModel _parentDecisionModel, String _branchedDecisionSymbol) throws MaltChainedException {
042                this.guide = _guide;
043                this.parentDecisionModel =_parentDecisionModel;
044                this.decisionIndex = parentDecisionModel.getDecisionIndex() + 1;
045                if (_branchedDecisionSymbol != null && _branchedDecisionSymbol.length() > 0) {
046                        this.branchedDecisionSymbols = _branchedDecisionSymbol;
047                        this.modelName = "bdm"+decisionIndex+branchedDecisionSymbols;
048                } else {
049                        this.branchedDecisionSymbols = "";
050                        this.modelName = "bdm"+decisionIndex;
051                }
052//              this.featureModel = parentDecisionModel.getFeatureModel();
053                this.children = new HashMap<Integer,DecisionModel>();
054        }
055        
056        private void initInstanceModel(FeatureModel featureModel, String subModelName) throws MaltChainedException {
057                if (featureModel.hasDivideFeatureFunction()) {
058                        instanceModel = new FeatureDivideModel(this);
059                } else {
060                        instanceModel = new AtomicModel(-1, this);
061                }
062        }
063        
064//      public void updateFeatureModel() throws MaltChainedException {
065//              featureModel.update();
066//      }
067
068        public void finalizeSentence(DependencyStructure dependencyGraph) throws MaltChainedException {
069                if (instanceModel != null) {
070                        instanceModel.finalizeSentence(dependencyGraph);
071                }
072                if (children != null) {
073                        for (DecisionModel child : children.values()) {
074                                child.finalizeSentence(dependencyGraph);
075                        }
076                }
077        }
078        
079        public void noMoreInstances(FeatureModel featureModel) throws MaltChainedException {
080                if (guide.getGuideMode() == ClassifierGuide.GuideMode.CLASSIFY) {
081                        throw new GuideException("The decision model could not create it's model. ");
082                }
083                if (instanceModel != null) {
084                        instanceModel.noMoreInstances(featureModel);
085                        instanceModel.train();
086                }
087                if (children != null) {
088                        for (DecisionModel child : children.values()) {
089                                child.noMoreInstances(featureModel);
090                        }
091                }
092        }
093
094        public void terminate() throws MaltChainedException {
095                if (instanceModel != null) {
096                        instanceModel.terminate();
097                        instanceModel = null;
098                }
099                if (children != null) {
100                        for (DecisionModel child : children.values()) {
101                                child.terminate();
102                        }
103                }
104        }
105        
106        public void addInstance(FeatureModel featureModel, GuideDecision decision) throws MaltChainedException {
107                if (decision instanceof SingleDecision) {
108                        throw new GuideException("A branched decision model expect more than one decisions. ");
109                }
110                featureModel.update();
111                final SingleDecision singleDecision = ((MultipleDecision)decision).getSingleDecision(decisionIndex);
112                if (instanceModel == null) {
113                        initInstanceModel(featureModel,singleDecision.getTableContainer().getTableContainerName());
114                }
115                
116                instanceModel.addInstance(featureModel.getFeatureVector(branchedDecisionSymbols, singleDecision.getTableContainer().getTableContainerName()), singleDecision);
117                if (decisionIndex+1 < decision.numberOfDecisions()) {
118                        if (singleDecision.continueWithNextDecision()) {
119                                DecisionModel child = children.get(singleDecision.getDecisionCode());
120                                if (child == null) {
121                                        child = initChildDecisionModel(((MultipleDecision)decision).getSingleDecision(decisionIndex+1), 
122                                                        branchedDecisionSymbols+(branchedDecisionSymbols.length() == 0?"":"_")+singleDecision.getDecisionSymbol());
123                                        children.put(singleDecision.getDecisionCode(), child);
124                                }
125                                child.addInstance(featureModel,decision);
126                        }
127                }
128        }
129        
130        public boolean predict(FeatureModel featureModel, GuideDecision decision) throws MaltChainedException {
131                featureModel.update();
132                final SingleDecision singleDecision = ((MultipleDecision)decision).getSingleDecision(decisionIndex);
133                if (instanceModel == null) {
134                        initInstanceModel(featureModel, singleDecision.getTableContainer().getTableContainerName());
135                }
136                instanceModel.predict(featureModel.getFeatureVector(branchedDecisionSymbols, singleDecision.getTableContainer().getTableContainerName()), singleDecision);
137                if (decisionIndex+1 < decision.numberOfDecisions()) {
138                        if (singleDecision.continueWithNextDecision()) {
139                                DecisionModel child = children.get(singleDecision.getDecisionCode());
140                                if (child == null) {
141                                        child = initChildDecisionModel(((MultipleDecision)decision).getSingleDecision(decisionIndex+1), 
142                                                        branchedDecisionSymbols+(branchedDecisionSymbols.length() == 0?"":"_")+singleDecision.getDecisionSymbol());
143                                        children.put(singleDecision.getDecisionCode(), child);
144                                }
145                                child.predict(featureModel, decision);
146                        }
147                }
148
149                return true;
150        }
151        
152        public FeatureVector predictExtract(FeatureModel featureModel, GuideDecision decision) throws MaltChainedException {
153                if (decision instanceof SingleDecision) {
154                        throw new GuideException("A branched decision model expect more than one decisions. ");
155                }
156                featureModel.update();
157                final SingleDecision singleDecision = ((MultipleDecision)decision).getSingleDecision(decisionIndex);
158                if (instanceModel == null) {
159                        initInstanceModel(featureModel, singleDecision.getTableContainer().getTableContainerName());
160                }
161                FeatureVector fv = instanceModel.predictExtract(featureModel.getFeatureVector(branchedDecisionSymbols, singleDecision.getTableContainer().getTableContainerName()), singleDecision);
162                if (decisionIndex+1 < decision.numberOfDecisions()) {
163                        if (singleDecision.continueWithNextDecision()) {
164                                DecisionModel child = children.get(singleDecision.getDecisionCode());
165                                if (child == null) {
166                                        child = initChildDecisionModel(((MultipleDecision)decision).getSingleDecision(decisionIndex+1), 
167                                                        branchedDecisionSymbols+(branchedDecisionSymbols.length() == 0?"":"_")+singleDecision.getDecisionSymbol());
168                                        children.put(singleDecision.getDecisionCode(), child);
169                                }
170                                child.predictExtract(featureModel, decision);
171                        }
172                }
173
174                return fv;
175        }
176        
177        public FeatureVector extract(FeatureModel featureModel) throws MaltChainedException {
178                featureModel.update();
179                return null; //instanceModel.extract(); // TODO handle many feature vectors
180        }
181        
182        public boolean predictFromKBestList(FeatureModel featureModel, GuideDecision decision) throws MaltChainedException {
183                if (decision instanceof SingleDecision) {
184                        throw new GuideException("A branched decision model expect more than one decisions. ");
185                }
186                
187                boolean success = false;
188                final SingleDecision singleDecision = ((MultipleDecision)decision).getSingleDecision(decisionIndex);
189                if (decisionIndex+1 < decision.numberOfDecisions()) {
190                        if (singleDecision.continueWithNextDecision()) {
191                                DecisionModel child = children.get(singleDecision.getDecisionCode());
192                                if (child != null) {
193                                        success = child.predictFromKBestList(featureModel, decision);
194                                }
195                                
196                        }
197                }
198                if (!success) {
199                        success = singleDecision.updateFromKBestList();
200                        if (decisionIndex+1 < decision.numberOfDecisions()) {
201                                if (singleDecision.continueWithNextDecision()) {
202                                        DecisionModel child = children.get(singleDecision.getDecisionCode());
203                                        if (child == null) {
204                                                child = initChildDecisionModel(((MultipleDecision)decision).getSingleDecision(decisionIndex+1), 
205                                                                branchedDecisionSymbols+(branchedDecisionSymbols.length() == 0?"":"_")+singleDecision.getDecisionSymbol());
206                                                children.put(singleDecision.getDecisionCode(), child);
207                                        }
208                                        child.predict(featureModel, decision);
209                                }
210                        }
211                }
212                return success;
213        }
214        
215
216        public ClassifierGuide getGuide() {
217                return guide;
218        }
219
220        public String getModelName() {
221                return modelName;
222        }
223        
224//      public FeatureModel getFeatureModel() {
225//              return featureModel;
226//      }
227
228        public int getDecisionIndex() {
229                return decisionIndex;
230        }
231
232        public DecisionModel getParentDecisionModel() {
233                return parentDecisionModel;
234        }
235        
236        private DecisionModel initChildDecisionModel(SingleDecision decision, String branchedDecisionSymbol) throws MaltChainedException {
237                if (decision.getRelationToNextDecision() == RelationToNextDecision.SEQUANTIAL) {
238                        return new SeqDecisionModel(guide, this, branchedDecisionSymbol);
239                } else if (decision.getRelationToNextDecision() == RelationToNextDecision.BRANCHED) {
240                        return new BranchedDecisionModel(guide, this, branchedDecisionSymbol);
241                } else if (decision.getRelationToNextDecision() == RelationToNextDecision.NONE) {
242                        return new OneDecisionModel(guide, this, branchedDecisionSymbol);
243                }
244                throw new GuideException("Could not find an appropriate decision model for the relation to the next decision"); 
245        }
246        
247        public String toString() {
248                final StringBuilder sb = new StringBuilder();
249                sb.append(modelName + ", ");
250                for (DecisionModel model : children.values()) {
251                        sb.append(model.toString() + ", ");
252                }
253                return sb.toString();
254        }
255}