    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    }