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.Guide;
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() == Guide.GuideMode.TRAIN && index == -1) {
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            public void noMoreInstances() throws MaltChainedException {
074                    try {
075                            method.noMoreInstances();
076                    } catch (NullPointerException e) {
077                            throw new GuideException("The learner cannot be found. ", e);
078                    }
079            }
080            
081            public void finalizeSentence(DependencyStructure dependencyGraph) throws MaltChainedException {
082                    try {
083                            method.finalizeSentence(dependencyGraph);
084                    } catch (NullPointerException e) {
085                            throw new GuideException("The learner cannot be found. ", e);
086                    }
087            }
088    
089            public boolean predict(SingleDecision decision) throws MaltChainedException {
090                    try {
091                            if (getGuide().getGuideMode() != Guide.GuideMode.CLASSIFY) {
092                                    throw new GuideException("Can only predict during parsing. ");
093                            }
094                            return method.predict(featureVector, decision);
095                    } catch (NullPointerException e) {
096                            throw new GuideException("The learner cannot be found. ", e);
097                    }
098            }
099    
100            public void terminate() throws MaltChainedException {
101                    if (method != null) {
102                            method.terminate();
103                            method = null;
104                    }
105                    featureVector = null;
106                    parent = null;
107            }
108            
109            /**
110             * Moves all instance from this atomic model into the destination atomic model and add the divide feature.
111             * This method is used by the feature divide model to sum up all model below a certain threshold.
112             * 
113             * @param model the destination atomic model 
114             * @param divideFeature the divide feature
115             * @param divideFeatureIndexVector the divide feature index vector
116             * @throws MaltChainedException
117             */
118            public void moveAllInstances(AtomicModel model, FeatureFunction divideFeature, ArrayList<Integer> divideFeatureIndexVector) throws MaltChainedException {
119                    if (method == null) {
120                            throw new GuideException("The learner cannot be found. ");
121                    } else if (model == null) {
122                            throw new GuideException("The guide model cannot be found. ");
123                    } else if (divideFeature == null) {
124                            throw new GuideException("The divide feature cannot be found. ");
125                    } else if (divideFeatureIndexVector == null) {
126                            throw new GuideException("The divide feature index vector cannot be found. ");
127                    }
128                    ((Modifiable)divideFeature).setFeatureValue(index);
129                    method.moveAllInstances(model.getMethod(), divideFeature, divideFeatureIndexVector);
130                    method.terminate();
131                    method = null;
132            }
133            
134            /**
135             * Invokes the train() of the learning method 
136             * 
137             * @throws MaltChainedException
138             */
139            public void train() throws MaltChainedException {
140                    try {
141                            method.train(featureVector);
142                            method.terminate();
143                            method = null;
144                    } catch (NullPointerException e) {      
145                            throw new GuideException("The learner cannot be found. ", e);
146                    }
147            }
148            
149            /**
150             * Initialize the learning method according to the option --learner-method.
151             * 
152             * @throws MaltChainedException
153             */
154            public void initMethod() throws MaltChainedException {
155                    Class<?> clazz = (Class<?>)getGuide().getConfiguration().getOptionValue("guide", "learner");
156                    if (clazz == org.maltparser.ml.libsvm.Libsvm.class && (Boolean)getGuide().getConfiguration().getOptionValue("malt0.4", "behavior") == true) {
157                            try {
158                                    clazz = Class.forName("org.maltparser.ml.libsvm.malt04.LibsvmMalt04");
159                            } catch (ClassNotFoundException e) {
160                                    throw new GuideException("Could not find the class 'org.maltparser.ml.libsvm.malt04.LibsvmMalt04'. ", e);
161                            }
162                    }
163                    Class<?>[] argTypes = { org.maltparser.parser.guide.instance.InstanceModel.class, java.lang.Integer.class };
164                    Object[] arguments = new Object[2];
165                    arguments[0] = this;
166                    if (getGuide().getGuideMode() == Guide.GuideMode.CLASSIFY) {
167                            arguments[1] = LearningMethod.CLASSIFY;
168                    } else if (getGuide().getGuideMode() == Guide.GuideMode.TRAIN) {
169                            arguments[1] = LearningMethod.TRAIN;
170                    }
171    
172                    try {   
173                            Constructor<?> constructor = clazz.getConstructor(argTypes);
174                            this.method = (LearningMethod)constructor.newInstance(arguments);
175                    } catch (NoSuchMethodException e) {
176                            throw new GuideException("The learner class '"+clazz.getName()+"' cannot be initialized. ", e);
177                    } catch (InstantiationException e) {
178                            throw new GuideException("The learner class '"+clazz.getName()+"' cannot be initialized. ", e);
179                    } catch (IllegalAccessException e) {
180                            throw new GuideException("The learner class '"+clazz.getName()+"' cannot be initialized. ", e);
181                    } catch (InvocationTargetException e) {
182                            throw new GuideException("The learner class '"+clazz.getName()+"' cannot be initialized. ", e);
183                    }
184            }
185            
186            
187            
188            /**
189             * Returns the parent guide model
190             * 
191             * @return the parent guide model
192             */
193            public Model getParent() throws MaltChainedException {
194                    if (parent == null) {
195                            throw new GuideException("The atomic model can only be used by a parent model. ");
196                    }
197                    return parent;
198            }
199    
200            /**
201             * Sets the parent guide model
202             * 
203             * @param parent the parent guide model
204             */
205            protected void setParent(Model parent) {
206                    this.parent = parent;
207            }
208    
209            public String getModelName() {
210                    return modelName;
211            }
212    
213            /**
214             * Sets the name of the atomic model
215             * 
216             * @param modelName the name of the atomic model
217             */
218            protected void setModelName(String modelName) {
219                    this.modelName = modelName;
220            }
221    
222            /**
223             * Returns the feature vector used by this atomic model
224             * 
225             * @return a feature vector object
226             */
227            public FeatureVector getFeatures() {
228                    return featureVector;
229            }
230    
231            /**
232             * Sets the feature vector used by the atomic model.
233             * 
234             * @param features a feature vector object
235             */
236            protected void setFeatures(FeatureVector features) {
237                    this.featureVector = features;
238            }
239    
240            public Guide getGuide() {
241                    return parent.getGuide();
242            }
243            
244            /**
245             * Returns the index of the atomic model
246             * 
247             * @return the index of the atomic model
248             */
249            public int getIndex() {
250                    return index;
251            }
252    
253            /**
254             * Sets the index of the model (-1..n), where -1 is a special value.
255             * 
256             * @param index index value (-1..n) of the atomic model
257             */
258            protected void setIndex(int index) {
259                    this.index = index;
260            }
261    
262            /**
263             * Returns the frequency (number of instances)
264             * 
265             * @return the frequency (number of instances)
266             */
267            public int getFrequency() {
268                    return frequency;
269            }
270            
271            /**
272             * Increase the frequency by 1
273             */
274            public void increaseFrequency() {
275                    if (parent instanceof InstanceModel) {
276                            ((InstanceModel)parent).increaseFrequency();
277                    }
278                    frequency++;
279            }
280            
281            public void decreaseFrequency() {
282                    if (parent instanceof InstanceModel) {
283                            ((InstanceModel)parent).decreaseFrequency();
284                    }
285                    frequency--;
286            }
287            /**
288             * Sets the frequency (number of instances)
289             * 
290             * @param frequency (number of instances)
291             */
292            protected void setFrequency(int frequency) {
293                    this.frequency = frequency;
294            }
295            
296            /**
297             * Returns a learner object
298             * 
299             * @return a learner object
300             */
301            public LearningMethod getMethod() {
302                    return method;
303            }
304            
305            
306            /* (non-Javadoc)
307             * @see java.lang.Object#toString()
308             */
309            public String toString() {
310                    final StringBuilder sb = new StringBuilder();
311                    sb.append(method.toString());
312                    return sb.toString();
313            }
314    }