    001    package org.maltparser.parser.guide.instance;
    002
    003    import java.io.IOException;
    004    import java.lang.reflect.Constructor;
    005    import java.lang.reflect.InvocationTargetException;
    006    import java.util.ArrayList;
    007    import java.util.Formatter;
    008
    009    import org.maltparser.core.exception.MaltChainedException;
    010    import org.maltparser.core.feature.FeatureVector;
    011    import org.maltparser.core.feature.function.FeatureFunction;
    012    import org.maltparser.core.feature.function.Modifiable;
    013    import org.maltparser.core.syntaxgraph.DependencyStructure;
    014    import org.maltparser.ml.LearningMethod;
    015    import org.maltparser.parser.guide.ClassifierGuide;
    016    import org.maltparser.parser.guide.GuideException;
    017    import org.maltparser.parser.guide.Model;
    018    import org.maltparser.parser.history.action.SingleDecision;
    019
    020
    021    /**
    022
    023    @author Johan Hall
    024    @since 1.0
    025    */
    026    public class AtomicModel implements InstanceModel {
    027            private Model parent;
    028            private String modelName;
    029            private FeatureVector featureVector;
    030            private int index;
    031            private int frequency = 0;
    032            private LearningMethod method;
    033
    034
    035            /**
    036             * Constructs an atomic model.
    037             *
    038             * @param index the index of the atomic model (-1..n), where -1 is special value (used by a single model
    039             * or the master divide model) and n is number of divide models.
    040             * @param features the feature vector used by the atomic model.
    041             * @param parent the parent guide model.
    042             * @throws MaltChainedException
    043             */
    044            public AtomicModel(int index, FeatureVector features, Model parent) throws MaltChainedException {
    045                    setParent(parent);
    046                    setIndex(index);
    047                    if (index == -1) {
    048                            setModelName(parent.getModelName()+".");
    049                    } else {
    050                            setModelName(parent.getModelName()+"."+new Formatter().format("%03d", index)+".");
    051                    }
    052                    setFeatures(features);
    053                    setFrequency(0);
    054                    initMethod();
    055                    if (getGuide().getGuideMode() == ClassifierGuide.GuideMode.BATCH && index == -1 && getGuide().getConfiguration().getConfigurationDir().getInfoFileWriter() != null) {
    056                            try {
    057                                    getGuide().getConfiguration().getConfigurationDir().getInfoFileWriter().write(method.toString());
    058                                    getGuide().getConfiguration().getConfigurationDir().getInfoFileWriter().flush();
    059                            } catch (IOException e) {
    060                                    throw new GuideException("Could not write learner settings to the information file. ", e);
    061                            }
    062                    }
    063            }
    064
    065            public void addInstance(SingleDecision decision) throws MaltChainedException {
    066                    try {
    067                            method.addInstance(decision, featureVector);
    068                    } catch (NullPointerException e) {
    069                            throw new GuideException("The learner cannot be found. ", e);
    070                    }
    071            }
    072
    073
    074            public void noMoreInstances() throws MaltChainedException {
    075                    try {
    076                            method.noMoreInstances();
    077                    } catch (NullPointerException e) {
    078                            throw new GuideException("The learner cannot be found. ", e);
    079                    }
    080            }
    081
    082            public void finalizeSentence(DependencyStructure dependencyGraph) throws MaltChainedException {
    083                    try {
    084                            method.finalizeSentence(dependencyGraph);
    085                    } catch (NullPointerException e) {
    086                            throw new GuideException("The learner cannot be found. ", e);
    087                    }
    088            }
    089
    090            public boolean predict(SingleDecision decision) throws MaltChainedException {
    091                    try {
    092                            if (getGuide().getGuideMode() == ClassifierGuide.GuideMode.BATCH) {
    093                                    throw new GuideException("Cannot predict during batch training. ");
    094                            }
    095                            return method.predict(featureVector, decision);
    096                    } catch (NullPointerException e) {
    097                            throw new GuideException("The learner cannot be found. ", e);
    098                    }
    099            }
    100
    101            public FeatureVector predictExtract(SingleDecision decision) throws MaltChainedException {
    102                    try {
    103                            if (getGuide().getGuideMode() == ClassifierGuide.GuideMode.BATCH) {
    104                                    throw new GuideException("Cannot predict during batch training. ");
    105                            }
    106                            if (method.predict(featureVector, decision)) {
    107                                    return featureVector;
    108                            }
    109                            return null;
    110                    } catch (NullPointerException e) {
    111                            throw new GuideException("The learner cannot be found. ", e);
    112                    }
    113            }
    114
    115            public FeatureVector extract() throws MaltChainedException {
    116                    return featureVector;
    117            }
    118
    119            public void terminate() throws MaltChainedException {
    120                    if (method != null) {
    121                            method.terminate();
    122                            method = null;
    123                    }
    124                    featureVector = null;
    125                    parent = null;
    126            }
    127
    128            /**
    129             * Moves all instance from this atomic model into the destination atomic model and add the divide feature.
    130             * This method is used by the feature divide model to sum up all model below a certain threshold.
    131             *
    132             * @param model the destination atomic model
    133             * @param divideFeature the divide feature
    134             * @param divideFeatureIndexVector the divide feature index vector
    135             * @throws MaltChainedException
    136             */
    137            public void moveAllInstances(AtomicModel model, FeatureFunction divideFeature, ArrayList<Integer> divideFeatureIndexVector) throws MaltChainedException {
    138                    if (method == null) {
    139                            throw new GuideException("The learner cannot be found. ");
    140                    } else if (model == null) {
    141                            throw new GuideException("The guide model cannot be found. ");
    142                    } else if (divideFeature == null) {
    143                            throw new GuideException("The divide feature cannot be found. ");
    144                    } else if (divideFeatureIndexVector == null) {
    145                            throw new GuideException("The divide feature index vector cannot be found. ");
    146                    }
    147                    ((Modifiable)divideFeature).setFeatureValue(index);
    148                    method.moveAllInstances(model.getMethod(), divideFeature, divideFeatureIndexVector);
    149                    method.terminate();
    150                    method = null;
    151            }
    152
    153            /**
    154             * Invokes the train() of the learning method
    155             *
    156             * @throws MaltChainedException
    157             */
    158            public void train() throws MaltChainedException {
    159                    try {
    160                            method.train(featureVector);
    161                            method.terminate();
    162                            method = null;
    163
    164                    } catch (NullPointerException e) {
    165                            throw new GuideException("The learner cannot be found. ", e);
    166                    }
    167
    168
    169            }
    170
    171            /**
    172             * Initialize the learning method according to the option --learner-method.
    173             *
    174             * @throws MaltChainedException
    175             */
    176            public void initMethod() throws MaltChainedException {
    177                    Class<?> clazz = (Class<?>)getGuide().getConfiguration().getOptionValue("guide", "learner");
    178                    Class<?>[] argTypes = { org.maltparser.parser.guide.instance.InstanceModel.class, java.lang.Integer.class };
    179                    Object[] arguments = new Object[2];
    180                    arguments[0] = this;
    181                    if (getGuide().getGuideMode() == ClassifierGuide.GuideMode.CLASSIFY) {
    182                            arguments[1] = LearningMethod.CLASSIFY;
    183                    } else if (getGuide().getGuideMode() == ClassifierGuide.GuideMode.BATCH) {
    184                            arguments[1] = LearningMethod.BATCH;
    185                    }
    186
    187                    try {
    188                            Constructor<?> constructor = clazz.getConstructor(argTypes);
    189                            this.method = (LearningMethod)constructor.newInstance(arguments);
    190                    } catch (NoSuchMethodException e) {
    191                            throw new GuideException("The learner class '"+clazz.getName()+"' cannot be initialized. ", e);
    192                    } catch (InstantiationException e) {
    193                            throw new GuideException("The learner class '"+clazz.getName()+"' cannot be initialized. ", e);
    194                    } catch (IllegalAccessException e) {
    195                            throw new GuideException("The learner class '"+clazz.getName()+"' cannot be initialized. ", e);
    196                    } catch (InvocationTargetException e) {
    197                            throw new GuideException("The learner class '"+clazz.getName()+"' cannot be initialized. ", e);
    198                    }
    199            }
    200
    201
    202
    203            /**
    204             * Returns the parent guide model
    205             *
    206             * @return the parent guide model
    207             */
    208            public Model getParent() throws MaltChainedException {
    209                    if (parent == null) {
    210                            throw new GuideException("The atomic model can only be used by a parent model. ");
    211                    }
    212                    return parent;
    213            }
    214
    215            /**
    216             * Sets the parent guide model
    217             *
    218             * @param parent the parent guide model
    219             */
    220            protected void setParent(Model parent) {
    221                    this.parent = parent;
    222            }
    223
    224            public String getModelName() {
    225                    return modelName;
    226            }
    227
    228            /**
    229             * Sets the name of the atomic model
    230             *
    231             * @param modelName the name of the atomic model
    232             */
    233            protected void setModelName(String modelName) {
    234                    this.modelName = modelName;
    235            }
    236
    237            /**
    238             * Returns the feature vector used by this atomic model
    239             *
    240             * @return a feature vector object
    241             */
    242            public FeatureVector getFeatures() {
    243                    return featureVector;
    244            }
    245
    246            /**
    247             * Sets the feature vector used by the atomic model.
    248             *
    249             * @param features a feature vector object
    250             */
    251            protected void setFeatures(FeatureVector features) {
    252                    this.featureVector = features;
    253            }
    254
    255            public ClassifierGuide getGuide() {
    256                    return parent.getGuide();
    257            }
    258
    259            /**
    260             * Returns the index of the atomic model
    261             *
    262             * @return the index of the atomic model
    263             */
    264            public int getIndex() {
    265                    return index;
    266            }
    267
    268            /**
    269             * Sets the index of the model (-1..n), where -1 is a special value.
    270             *
    271             * @param index index value (-1..n) of the atomic model
    272             */
    273            protected void setIndex(int index) {
    274                    this.index = index;
    275            }
    276
    277            /**
    278             * Returns the frequency (number of instances)
    279             *
    280             * @return the frequency (number of instances)
    281             */
    282            public int getFrequency() {
    283                    return frequency;
    284            }
    285
    286            /**
    287             * Increase the frequency by 1
    288             */
    289            public void increaseFrequency() {
    290                    if (parent instanceof InstanceModel) {
    291                            ((InstanceModel)parent).increaseFrequency();
    292                    }
    293                    frequency++;
    294            }
    295
    296            public void decreaseFrequency() {
    297                    if (parent instanceof InstanceModel) {
    298                            ((InstanceModel)parent).decreaseFrequency();
    299                    }
    300                    frequency--;
    301            }
    302            /**
    303             * Sets the frequency (number of instances)
    304             *
    305             * @param frequency (number of instances)
    306             */
    307            protected void setFrequency(int frequency) {
    308                    this.frequency = frequency;
    309            }
    310
    311            /**
    312             * Returns a learner object
    313             *
    314             * @return a learner object
    315             */
    316            public LearningMethod getMethod() {
    317                    return method;
    318            }
    319
    320
    321            /* (non-Javadoc)
    322             * @see java.lang.Object#toString()
    323             */
    324            public String toString() {
    325                    final StringBuilder sb = new StringBuilder();
    326                    sb.append(method.toString());
    327                    return sb.toString();
    328            }
    329    }