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 }