source: src/main/java/weka/classifiers/functions/LibSVM.java @ 18

Last change on this file since 18 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 48.1 KB
Line 
1/*
2 *    This program is free software; you can redistribute it and/or modify
3 *    it under the terms of the GNU General Public License as published by
4 *    the Free Software Foundation; either version 2 of the License, or
5 *    (at your option) any later version.
6 *
7 *    This program is distributed in the hope that it will be useful,
8 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 *    GNU General Public License for more details.
11 *
12 *    You should have received a copy of the GNU General Public License
13 *    along with this program; if not, write to the Free Software
14 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17/*
18 * LibSVM.java
19 * Copyright (C) 2005 Yasser EL-Manzalawy (original code)
20 * Copyright (C) 2005 University of Waikato, Hamilton, NZ (adapted code)
21 *
22 */
23
24package weka.classifiers.functions;
25
26import weka.classifiers.Classifier;
27import weka.classifiers.AbstractClassifier;
28import weka.core.Capabilities;
29import weka.core.Instance;
30import weka.core.Instances;
31import weka.core.Option;
32import weka.core.RevisionUtils;
33import weka.core.SelectedTag;
34import weka.core.Tag;
35import weka.core.TechnicalInformation;
36import weka.core.TechnicalInformationHandler;
37import weka.core.Utils;
38import weka.core.Capabilities.Capability;
39import weka.core.TechnicalInformation.Type;
40import weka.filters.Filter;
41import weka.filters.unsupervised.attribute.Normalize;
42import weka.filters.unsupervised.attribute.ReplaceMissingValues;
43
44import java.io.File;
45import java.lang.reflect.Array;
46import java.lang.reflect.Field;
47import java.lang.reflect.Method;
48import java.util.Enumeration;
49import java.util.StringTokenizer;
50import java.util.Vector;
51
52/*
53 * Modifications by FracPete:
54 * - complete overhaul to make it useable in Weka
55 * - accesses libsvm classes only via Reflection to make Weka compile without
56 *   the libsvm classes
57 * - uses more efficient code to transfer the data into the libsvm sparse format
58 */
59
60/**
61 <!-- globalinfo-start -->
62 * A wrapper class for the libsvm tools (the libsvm classes, typically the jar file, need to be in the classpath to use this classifier).<br/>
63 * LibSVM runs faster than SMO since it uses LibSVM to build the SVM classifier.<br/>
64 * LibSVM allows users to experiment with One-class SVM, Regressing SVM, and nu-SVM supported by LibSVM tool. LibSVM reports many useful statistics about LibSVM classifier (e.g., confusion matrix,precision, recall, ROC score, etc.).<br/>
65 * <br/>
66 * Yasser EL-Manzalawy (2005). WLSVM. URL http://www.cs.iastate.edu/~yasser/wlsvm/.<br/>
67 * <br/>
68 * Chih-Chung Chang, Chih-Jen Lin (2001). LIBSVM - A Library for Support Vector Machines. URL http://www.csie.ntu.edu.tw/~cjlin/libsvm/.
69 * <p/>
70 <!-- globalinfo-end -->
71 *
72 <!-- technical-bibtex-start -->
73 * BibTeX:
74 * <pre>
75 * &#64;misc{EL-Manzalawy2005,
76 *    author = {Yasser EL-Manzalawy},
77 *    note = {You don't need to include the WLSVM package in the CLASSPATH},
78 *    title = {WLSVM},
79 *    year = {2005},
80 *    URL = {http://www.cs.iastate.edu/\~yasser/wlsvm/}
81 * }
82 *
83 * &#64;misc{Chang2001,
84 *    author = {Chih-Chung Chang and Chih-Jen Lin},
85 *    note = {The Weka classifier works with version 2.82 of LIBSVM},
86 *    title = {LIBSVM - A Library for Support Vector Machines},
87 *    year = {2001},
88 *    URL = {http://www.csie.ntu.edu.tw/\~cjlin/libsvm/}
89 * }
90 * </pre>
91 * <p/>
92 <!-- technical-bibtex-end -->
93 *
94 <!-- options-start -->
95 * Valid options are: <p/>
96 *
97 * <pre> -S &lt;int&gt;
98 *  Set type of SVM (default: 0)
99 *    0 = C-SVC
100 *    1 = nu-SVC
101 *    2 = one-class SVM
102 *    3 = epsilon-SVR
103 *    4 = nu-SVR</pre>
104 *
105 * <pre> -K &lt;int&gt;
106 *  Set type of kernel function (default: 2)
107 *    0 = linear: u'*v
108 *    1 = polynomial: (gamma*u'*v + coef0)^degree
109 *    2 = radial basis function: exp(-gamma*|u-v|^2)
110 *    3 = sigmoid: tanh(gamma*u'*v + coef0)</pre>
111 *
112 * <pre> -D &lt;int&gt;
113 *  Set degree in kernel function (default: 3)</pre>
114 *
115 * <pre> -G &lt;double&gt;
116 *  Set gamma in kernel function (default: 1/k)</pre>
117 *
118 * <pre> -R &lt;double&gt;
119 *  Set coef0 in kernel function (default: 0)</pre>
120 *
121 * <pre> -C &lt;double&gt;
122 *  Set the parameter C of C-SVC, epsilon-SVR, and nu-SVR
123 *   (default: 1)</pre>
124 *
125 * <pre> -N &lt;double&gt;
126 *  Set the parameter nu of nu-SVC, one-class SVM, and nu-SVR
127 *   (default: 0.5)</pre>
128 *
129 * <pre> -Z
130 *  Turns on normalization of input data (default: off)</pre>
131 *
132 * <pre> -J
133 *  Turn off nominal to binary conversion.
134 *  WARNING: use only if your data is all numeric!</pre>
135 *
136 * <pre> -V
137 *  Turn off missing value replacement.
138 *  WARNING: use only if your data has no missing values.</pre>
139 *
140 * <pre> -P &lt;double&gt;
141 *  Set the epsilon in loss function of epsilon-SVR (default: 0.1)</pre>
142 *
143 * <pre> -M &lt;double&gt;
144 *  Set cache memory size in MB (default: 40)</pre>
145 *
146 * <pre> -E &lt;double&gt;
147 *  Set tolerance of termination criterion (default: 0.001)</pre>
148 *
149 * <pre> -H
150 *  Turns the shrinking heuristics off (default: on)</pre>
151 *
152 * <pre> -W &lt;double&gt;
153 *  Set the parameters C of class i to weight[i]*C, for C-SVC.
154 *  E.g., for a 3-class problem, you could use "1 1 1" for equally
155 *  weighted classes.
156 *  (default: 1 for all classes)</pre>
157 *
158 * <pre> -B
159 *  Trains a SVC model instead of a SVR one (default: SVR)</pre>
160 *
161 * <pre> -model &lt;file&gt;
162 *  Specifies the filename to save the libsvm-internal model to.
163 *  Gets ignored if a directory is provided.</pre>
164 *
165 * <pre> -D
166 *  If set, classifier is run in debug mode and
167 *  may output additional info to the console</pre>
168 *
169 <!-- options-end -->
170 *
171 * @author  Yasser EL-Manzalawy
172 * @author  FracPete (fracpete at waikato dot ac dot nz)
173 * @version $Revision: 5928 $
174 * @see     weka.core.converters.LibSVMLoader
175 * @see     weka.core.converters.LibSVMSaver
176 */
177public class LibSVM 
178  extends AbstractClassifier
179  implements TechnicalInformationHandler {
180 
181  /** the svm classname. */
182  protected final static String CLASS_SVM = "libsvm.svm";
183 
184  /** the svm_model classname. */
185  protected final static String CLASS_SVMMODEL = "libsvm.svm_model";
186 
187  /** the svm_problem classname. */
188  protected final static String CLASS_SVMPROBLEM = "libsvm.svm_problem";
189 
190  /** the svm_parameter classname. */
191  protected final static String CLASS_SVMPARAMETER = "libsvm.svm_parameter";
192 
193  /** the svm_node classname. */
194  protected final static String CLASS_SVMNODE = "libsvm.svm_node";
195 
196  /** serial UID. */
197  protected static final long serialVersionUID = 14172;
198 
199  /** LibSVM Model. */
200  protected Object m_Model;
201 
202  /** for normalizing the data. */
203  protected Filter m_Filter = null;
204   
205  /** The filter used to get rid of missing values. */
206  protected ReplaceMissingValues m_ReplaceMissingValues;
207 
208  /** normalize input data. */
209  protected boolean m_Normalize = false;
210 
211  /** If true, the replace missing values filter is not applied. */
212  private boolean m_noReplaceMissingValues;
213 
214  /** SVM type C-SVC (classification). */
215  public static final int SVMTYPE_C_SVC = 0;
216  /** SVM type nu-SVC (classification). */
217  public static final int SVMTYPE_NU_SVC = 1;
218  /** SVM type one-class SVM (classification). */
219  public static final int SVMTYPE_ONE_CLASS_SVM = 2;
220  /** SVM type epsilon-SVR (regression). */
221  public static final int SVMTYPE_EPSILON_SVR = 3;
222  /** SVM type nu-SVR (regression). */
223  public static final int SVMTYPE_NU_SVR = 4;
224  /** SVM types. */
225  public static final Tag[] TAGS_SVMTYPE = {
226    new Tag(SVMTYPE_C_SVC, "C-SVC (classification)"),
227    new Tag(SVMTYPE_NU_SVC, "nu-SVC (classification)"),
228    new Tag(SVMTYPE_ONE_CLASS_SVM, "one-class SVM (classification)"),
229    new Tag(SVMTYPE_EPSILON_SVR, "epsilon-SVR (regression)"),
230    new Tag(SVMTYPE_NU_SVR, "nu-SVR (regression)")
231  };
232 
233  /** the SVM type. */
234  protected int m_SVMType = SVMTYPE_C_SVC;
235 
236  /** kernel type linear: u'*v. */
237  public static final int KERNELTYPE_LINEAR = 0;
238  /** kernel type polynomial: (gamma*u'*v + coef0)^degree. */
239  public static final int KERNELTYPE_POLYNOMIAL = 1;
240  /** kernel type radial basis function: exp(-gamma*|u-v|^2). */
241  public static final int KERNELTYPE_RBF = 2;
242  /** kernel type sigmoid: tanh(gamma*u'*v + coef0). */
243  public static final int KERNELTYPE_SIGMOID = 3;
244  /** the different kernel types. */
245  public static final Tag[] TAGS_KERNELTYPE = {
246    new Tag(KERNELTYPE_LINEAR, "linear: u'*v"),
247    new Tag(KERNELTYPE_POLYNOMIAL, "polynomial: (gamma*u'*v + coef0)^degree"),
248    new Tag(KERNELTYPE_RBF, "radial basis function: exp(-gamma*|u-v|^2)"),
249    new Tag(KERNELTYPE_SIGMOID, "sigmoid: tanh(gamma*u'*v + coef0)")
250  };
251 
252  /** the kernel type. */
253  protected int m_KernelType = KERNELTYPE_RBF;
254 
255  /** for poly - in older versions of libsvm declared as a double.
256   * At least since 2.82 it is an int. */
257  protected int m_Degree = 3;
258 
259  /** for poly/rbf/sigmoid. */
260  protected double m_Gamma = 0;
261 
262  /** for poly/rbf/sigmoid (the actual gamma). */
263  protected double m_GammaActual = 0;
264 
265  /** for poly/sigmoid. */
266  protected double m_Coef0 = 0;
267 
268  /** in MB. */
269  protected double m_CacheSize = 40;
270 
271  /** stopping criteria. */
272  protected double m_eps = 1e-3;
273 
274  /** cost, for C_SVC, EPSILON_SVR and NU_SVR. */
275  protected double m_Cost = 1;
276 
277  /** for C_SVC. */
278  protected int[] m_WeightLabel = new int[0];
279 
280  /** for C_SVC. */
281  protected double[] m_Weight = new double[0];
282 
283  /** for NU_SVC, ONE_CLASS, and NU_SVR. */
284  protected double m_nu = 0.5;
285 
286  /** loss, for EPSILON_SVR. */
287  protected double m_Loss = 0.1;
288 
289  /** use the shrinking heuristics. */
290  protected boolean m_Shrinking = true; 
291 
292  /** whether to generate probability estimates instead of +1/-1 in case of
293   * classification problems. */
294  protected boolean m_ProbabilityEstimates = false;
295 
296  /** the file to save the libsvm-internal model to. */
297  protected File m_ModelFile = new File(System.getProperty("user.dir"));
298   
299  /** whether the libsvm classes are in the Classpath. */
300  protected static boolean m_Present = false;
301  static {
302    try {
303      Class.forName(CLASS_SVM);
304      m_Present = true;
305    }
306    catch (Exception e) {
307      m_Present = false;
308    }
309  }
310 
311  /**
312   * Returns a string describing classifier.
313   *
314   * @return a description suitable for displaying in the
315   *         explorer/experimenter gui
316   */
317  public String globalInfo() {
318    return 
319      "A wrapper class for the libsvm tools (the libsvm classes, typically "
320    + "the jar file, need to be in the classpath to use this classifier).\n"
321    + "LibSVM runs faster than SMO since it uses LibSVM to build the SVM "
322    + "classifier.\n"
323    + "LibSVM allows users to experiment with One-class SVM, Regressing SVM, "
324    + "and nu-SVM supported by LibSVM tool. LibSVM reports many useful "
325    + "statistics about LibSVM classifier (e.g., confusion matrix,"
326    + "precision, recall, ROC score, etc.).\n"
327    + "\n"
328    + getTechnicalInformation().toString();
329  }
330
331  /**
332   * Returns an instance of a TechnicalInformation object, containing
333   * detailed information about the technical background of this class,
334   * e.g., paper reference or book this class is based on.
335   *
336   * @return the technical information about this class
337   */
338  public TechnicalInformation getTechnicalInformation() {
339    TechnicalInformation        result;
340    TechnicalInformation        additional;
341   
342    result = new TechnicalInformation(Type.MISC);
343    result.setValue(TechnicalInformation.Field.AUTHOR, "Yasser EL-Manzalawy");
344    result.setValue(TechnicalInformation.Field.YEAR, "2005");
345    result.setValue(TechnicalInformation.Field.TITLE, "WLSVM");
346    result.setValue(TechnicalInformation.Field.NOTE, "LibSVM was originally developed as 'WLSVM'");
347    result.setValue(TechnicalInformation.Field.URL, "http://www.cs.iastate.edu/~yasser/wlsvm/");
348    result.setValue(TechnicalInformation.Field.NOTE, "You don't need to include the WLSVM package in the CLASSPATH");
349   
350    additional = result.add(Type.MISC);
351    additional.setValue(TechnicalInformation.Field.AUTHOR, "Chih-Chung Chang and Chih-Jen Lin");
352    additional.setValue(TechnicalInformation.Field.TITLE, "LIBSVM - A Library for Support Vector Machines");
353    additional.setValue(TechnicalInformation.Field.YEAR, "2001");
354    additional.setValue(TechnicalInformation.Field.URL, "http://www.csie.ntu.edu.tw/~cjlin/libsvm/");
355    additional.setValue(TechnicalInformation.Field.NOTE, "The Weka classifier works with version 2.82 of LIBSVM");
356   
357    return result;
358  }
359 
360  /**
361   * Returns an enumeration describing the available options.
362   *
363   * @return an enumeration of all the available options.
364   */
365  public Enumeration listOptions() {
366    Vector      result;
367   
368    result = new Vector();
369   
370    result.addElement(
371        new Option(
372            "\tSet type of SVM (default: 0)\n"
373            + "\t\t 0 = C-SVC\n" 
374            + "\t\t 1 = nu-SVC\n"
375            + "\t\t 2 = one-class SVM\n" 
376            + "\t\t 3 = epsilon-SVR\n"
377            + "\t\t 4 = nu-SVR", 
378            "S", 1, "-S <int>"));
379   
380    result.addElement(
381        new Option(
382            "\tSet type of kernel function (default: 2)\n"
383            + "\t\t 0 = linear: u'*v\n"
384            + "\t\t 1 = polynomial: (gamma*u'*v + coef0)^degree\n"
385            + "\t\t 2 = radial basis function: exp(-gamma*|u-v|^2)\n"
386            + "\t\t 3 = sigmoid: tanh(gamma*u'*v + coef0)",
387            "K", 1, "-K <int>"));
388   
389    result.addElement(
390        new Option(
391            "\tSet degree in kernel function (default: 3)", 
392            "D", 1, "-D <int>"));
393   
394    result.addElement(
395        new Option(
396            "\tSet gamma in kernel function (default: 1/k)", 
397            "G", 1, "-G <double>"));
398   
399    result.addElement(
400        new Option(
401            "\tSet coef0 in kernel function (default: 0)", 
402            "R", 1, "-R <double>"));
403   
404    result.addElement(
405        new Option(
406            "\tSet the parameter C of C-SVC, epsilon-SVR, and nu-SVR\n"
407            + "\t (default: 1)",
408            "C", 1, "-C <double>"));
409   
410    result.addElement(
411        new Option(
412            "\tSet the parameter nu of nu-SVC, one-class SVM, and nu-SVR\n"
413            + "\t (default: 0.5)",
414            "N", 1, "-N <double>"));
415   
416    result.addElement(
417        new Option(
418            "\tTurns on normalization of input data (default: off)", 
419            "Z", 0, "-Z"));
420   
421    result.addElement(
422        new Option("\tTurn off nominal to binary conversion."
423            + "\n\tWARNING: use only if your data is all numeric!",
424            "J", 0, "-J"));
425   
426    result.addElement(
427        new Option("\tTurn off missing value replacement."
428            + "\n\tWARNING: use only if your data has no missing "
429            + "values.", "V", 0, "-V"));
430   
431    result.addElement(
432        new Option(
433            "\tSet the epsilon in loss function of epsilon-SVR (default: 0.1)",
434            "P", 1, "-P <double>"));
435   
436    result.addElement(
437        new Option(
438            "\tSet cache memory size in MB (default: 40)", 
439            "M", 1, "-M <double>"));
440   
441    result.addElement(
442        new Option(
443            "\tSet tolerance of termination criterion (default: 0.001)",
444            "E", 1, "-E <double>"));
445   
446    result.addElement(
447        new Option(
448            "\tTurns the shrinking heuristics off (default: on)",
449            "H", 0, "-H"));
450   
451    result.addElement(
452        new Option(
453            "\tSet the parameters C of class i to weight[i]*C, for C-SVC.\n" 
454            + "\tE.g., for a 3-class problem, you could use \"1 1 1\" for equally\n"
455            + "\tweighted classes.\n"
456            + "\t(default: 1 for all classes)",
457            "W", 1, "-W <double>"));
458   
459    result.addElement(
460        new Option(
461            "\tTrains a SVC model instead of a SVR one (default: SVR)",
462            "B", 0, "-B"));
463   
464    result.addElement(
465        new Option(
466            "\tSpecifies the filename to save the libsvm-internal model to.\n"
467            + "\tGets ignored if a directory is provided.",
468            "model", 1, "-model <file>"));
469
470    Enumeration en = super.listOptions();
471    while (en.hasMoreElements())
472      result.addElement(en.nextElement());
473   
474    return result.elements();
475  }
476 
477  /**
478   * Sets the classifier options <p/>
479   *
480   <!-- options-start -->
481   * Valid options are: <p/>
482   *
483   * <pre> -S &lt;int&gt;
484   *  Set type of SVM (default: 0)
485   *    0 = C-SVC
486   *    1 = nu-SVC
487   *    2 = one-class SVM
488   *    3 = epsilon-SVR
489   *    4 = nu-SVR</pre>
490   *
491   * <pre> -K &lt;int&gt;
492   *  Set type of kernel function (default: 2)
493   *    0 = linear: u'*v
494   *    1 = polynomial: (gamma*u'*v + coef0)^degree
495   *    2 = radial basis function: exp(-gamma*|u-v|^2)
496   *    3 = sigmoid: tanh(gamma*u'*v + coef0)</pre>
497   *
498   * <pre> -D &lt;int&gt;
499   *  Set degree in kernel function (default: 3)</pre>
500   *
501   * <pre> -G &lt;double&gt;
502   *  Set gamma in kernel function (default: 1/k)</pre>
503   *
504   * <pre> -R &lt;double&gt;
505   *  Set coef0 in kernel function (default: 0)</pre>
506   *
507   * <pre> -C &lt;double&gt;
508   *  Set the parameter C of C-SVC, epsilon-SVR, and nu-SVR
509   *   (default: 1)</pre>
510   *
511   * <pre> -N &lt;double&gt;
512   *  Set the parameter nu of nu-SVC, one-class SVM, and nu-SVR
513   *   (default: 0.5)</pre>
514   *
515   * <pre> -Z
516   *  Turns on normalization of input data (default: off)</pre>
517   *
518   * <pre> -J
519   *  Turn off nominal to binary conversion.
520   *  WARNING: use only if your data is all numeric!</pre>
521   *
522   * <pre> -V
523   *  Turn off missing value replacement.
524   *  WARNING: use only if your data has no missing values.</pre>
525   *
526   * <pre> -P &lt;double&gt;
527   *  Set the epsilon in loss function of epsilon-SVR (default: 0.1)</pre>
528   *
529   * <pre> -M &lt;double&gt;
530   *  Set cache memory size in MB (default: 40)</pre>
531   *
532   * <pre> -E &lt;double&gt;
533   *  Set tolerance of termination criterion (default: 0.001)</pre>
534   *
535   * <pre> -H
536   *  Turns the shrinking heuristics off (default: on)</pre>
537   *
538   * <pre> -W &lt;double&gt;
539   *  Set the parameters C of class i to weight[i]*C, for C-SVC.
540   *  E.g., for a 3-class problem, you could use "1 1 1" for equally
541   *  weighted classes.
542   *  (default: 1 for all classes)</pre>
543   *
544   * <pre> -B
545   *  Trains a SVC model instead of a SVR one (default: SVR)</pre>
546   *
547   * <pre> -model &lt;file&gt;
548   *  Specifies the filename to save the libsvm-internal model to.
549   *  Gets ignored if a directory is provided.</pre>
550   *
551   * <pre> -D
552   *  If set, classifier is run in debug mode and
553   *  may output additional info to the console</pre>
554   *
555   <!-- options-end -->
556   *
557   * @param options     the options to parse
558   * @throws Exception  if parsing fails
559   */
560  public void setOptions(String[] options) throws Exception {
561    String      tmpStr;
562   
563    tmpStr = Utils.getOption('S', options);
564    if (tmpStr.length() != 0)
565      setSVMType(
566          new SelectedTag(Integer.parseInt(tmpStr), TAGS_SVMTYPE));
567    else
568      setSVMType(
569          new SelectedTag(SVMTYPE_C_SVC, TAGS_SVMTYPE));
570   
571    tmpStr = Utils.getOption('K', options);
572    if (tmpStr.length() != 0)
573      setKernelType(
574          new SelectedTag(Integer.parseInt(tmpStr), TAGS_KERNELTYPE));
575    else
576      setKernelType(
577          new SelectedTag(KERNELTYPE_RBF, TAGS_KERNELTYPE));
578   
579    tmpStr = Utils.getOption('D', options);
580    if (tmpStr.length() != 0)
581      setDegree(Integer.parseInt(tmpStr));
582    else
583      setDegree(3);
584   
585    tmpStr = Utils.getOption('G', options);
586    if (tmpStr.length() != 0)
587      setGamma(Double.parseDouble(tmpStr));
588    else
589      setGamma(0);
590   
591    tmpStr = Utils.getOption('R', options);
592    if (tmpStr.length() != 0)
593      setCoef0(Double.parseDouble(tmpStr));
594    else
595      setCoef0(0);
596   
597    tmpStr = Utils.getOption('N', options);
598    if (tmpStr.length() != 0)
599      setNu(Double.parseDouble(tmpStr));
600    else
601      setNu(0.5);
602   
603    tmpStr = Utils.getOption('M', options);
604    if (tmpStr.length() != 0)
605      setCacheSize(Double.parseDouble(tmpStr));
606    else
607      setCacheSize(40);
608   
609    tmpStr = Utils.getOption('C', options);
610    if (tmpStr.length() != 0)
611      setCost(Double.parseDouble(tmpStr));
612    else
613      setCost(1);
614   
615    tmpStr = Utils.getOption('E', options);
616    if (tmpStr.length() != 0)
617      setEps(Double.parseDouble(tmpStr));
618    else
619      setEps(1e-3);
620   
621    setNormalize(Utils.getFlag('Z', options));
622   
623    setDoNotReplaceMissingValues(Utils.getFlag("V", options));
624   
625    tmpStr = Utils.getOption('P', options);
626    if (tmpStr.length() != 0)
627      setLoss(Double.parseDouble(tmpStr));
628    else
629      setLoss(0.1);
630   
631    setShrinking(!Utils.getFlag('H', options));
632   
633    setWeights(Utils.getOption('W', options));
634   
635    setProbabilityEstimates(Utils.getFlag('B', options));
636   
637    tmpStr = Utils.getOption("model", options);
638    if (tmpStr.length() == 0)
639      m_ModelFile = new File(System.getProperty("user.dir"));
640    else
641      m_ModelFile = new File(tmpStr);
642  }
643 
644  /**
645   * Returns the current options.
646   *
647   * @return            the current setup
648   */
649  public String[] getOptions() {
650    Vector        result;
651   
652    result  = new Vector();
653   
654    result.add("-S");
655    result.add("" + m_SVMType);
656   
657    result.add("-K");
658    result.add("" + m_KernelType);
659   
660    result.add("-D");
661    result.add("" + getDegree());
662   
663    result.add("-G");
664    result.add("" + getGamma());
665   
666    result.add("-R");
667    result.add("" + getCoef0());
668   
669    result.add("-N");
670    result.add("" + getNu());
671   
672    result.add("-M");
673    result.add("" + getCacheSize());
674   
675    result.add("-C");
676    result.add("" + getCost());
677   
678    result.add("-E");
679    result.add("" + getEps());
680   
681    result.add("-P");
682    result.add("" + getLoss());
683   
684    if (!getShrinking())
685      result.add("-H");
686   
687    if (getNormalize())
688      result.add("-Z");
689       
690    if (getDoNotReplaceMissingValues())
691      result.add("-V");
692   
693    if (getWeights().length() != 0) {
694      result.add("-W");
695      result.add("" + getWeights());
696    }
697   
698    if (getProbabilityEstimates())
699      result.add("-B");
700   
701    result.add("-model");
702    result.add(m_ModelFile.getAbsolutePath());
703   
704    return (String[]) result.toArray(new String[result.size()]);
705  }
706 
707  /**
708   * returns whether the libsvm classes are present or not, i.e. whether the
709   * classes are in the classpath or not
710   *
711   * @return whether the libsvm classes are available
712   */
713  public static boolean isPresent() {
714    return m_Present;
715  }
716 
717  /**
718   * Sets type of SVM (default SVMTYPE_C_SVC).
719   *
720   * @param value       the type of the SVM
721   */
722  public void setSVMType(SelectedTag value) {
723    if (value.getTags() == TAGS_SVMTYPE)
724      m_SVMType = value.getSelectedTag().getID();
725  }
726 
727  /**
728   * Gets type of SVM.
729   *
730   * @return            the type of the SVM
731   */
732  public SelectedTag getSVMType() {
733    return new SelectedTag(m_SVMType, TAGS_SVMTYPE);
734  }
735 
736  /**
737   * Returns the tip text for this property.
738   *
739   * @return tip text for this property suitable for
740   *         displaying in the explorer/experimenter gui
741   */
742  public String SVMTypeTipText() {
743    return "The type of SVM to use.";
744  }
745 
746  /**
747   * Sets type of kernel function (default KERNELTYPE_RBF).
748   *
749   * @param value       the kernel type
750   */
751  public void setKernelType(SelectedTag value) {
752    if (value.getTags() == TAGS_KERNELTYPE)
753      m_KernelType = value.getSelectedTag().getID();
754  }
755 
756  /**
757   * Gets type of kernel function.
758   *
759   * @return            the kernel type
760   */
761  public SelectedTag getKernelType() {
762    return new SelectedTag(m_KernelType, TAGS_KERNELTYPE);
763  }
764 
765  /**
766   * Returns the tip text for this property.
767   *
768   * @return tip text for this property suitable for
769   *         displaying in the explorer/experimenter gui
770   */
771  public String kernelTypeTipText() {
772    return "The type of kernel to use";
773  }
774 
775  /**
776   * Sets the degree of the kernel.
777   *
778   * @param value       the degree of the kernel
779   */
780  public void setDegree(int value) {
781    m_Degree = value;
782  }
783 
784  /**
785   * Gets the degree of the kernel.
786   *
787   * @return            the degree of the kernel
788   */
789  public int getDegree() {
790    return m_Degree;
791  }
792 
793  /**
794   * Returns the tip text for this property.
795   *
796   * @return tip text for this property suitable for
797   *         displaying in the explorer/experimenter gui
798   */
799  public String degreeTipText() {
800    return "The degree of the kernel.";
801  }
802 
803  /**
804   * Sets gamma (default = 1/no of attributes).
805   *
806   * @param value       the gamma value
807   */
808  public void setGamma(double value) {
809    m_Gamma = value;
810  }
811 
812  /**
813   * Gets gamma.
814   *
815   * @return            the current gamma
816   */
817  public double getGamma() {
818    return m_Gamma;
819  }
820 
821  /**
822   * Returns the tip text for this property.
823   *
824   * @return tip text for this property suitable for
825   *         displaying in the explorer/experimenter gui
826   */
827  public String gammaTipText() {
828    return "The gamma to use, if 0 then 1/max_index is used.";
829  }
830 
831  /**
832   * Sets coef (default 0).
833   *
834   * @param value       the coef
835   */
836  public void setCoef0(double value) {
837    m_Coef0 = value;
838  }
839 
840  /**
841   * Gets coef.
842   *
843   * @return            the coef
844   */
845  public double getCoef0() {
846    return m_Coef0;
847  }
848 
849  /**
850   * Returns the tip text for this property.
851   *
852   * @return tip text for this property suitable for
853   *         displaying in the explorer/experimenter gui
854   */
855  public String coef0TipText() {
856    return "The coefficient to use.";
857  }
858 
859  /**
860   * Sets nu of nu-SVC, one-class SVM, and nu-SVR (default 0.5).
861   *
862   * @param value       the new nu value
863   */
864  public void setNu(double value) {
865    m_nu = value;
866  }
867 
868  /**
869   * Gets nu of nu-SVC, one-class SVM, and nu-SVR (default 0.5).
870   *
871   * @return            the current nu value
872   */
873  public double getNu() {
874    return m_nu;
875  }
876 
877  /**
878   * Returns the tip text for this property.
879   *
880   * @return tip text for this property suitable for
881   *         displaying in the explorer/experimenter gui
882   */
883  public String nuTipText() {
884    return "The value of nu for nu-SVC, one-class SVM and nu-SVR.";
885  }
886 
887  /**
888   * Sets cache memory size in MB (default 40).
889   *
890   * @param value       the memory size in MB
891   */
892  public void setCacheSize(double value) {
893    m_CacheSize = value;
894  }
895 
896  /**
897   * Gets cache memory size in MB.
898   *
899   * @return            the memory size in MB
900   */
901  public double getCacheSize() {
902    return m_CacheSize;
903  }
904 
905  /**
906   * Returns the tip text for this property.
907   *
908   * @return tip text for this property suitable for
909   *         displaying in the explorer/experimenter gui
910   */
911  public String cacheSizeTipText() {
912    return "The cache size in MB.";
913  }
914 
915  /**
916   * Sets the parameter C of C-SVC, epsilon-SVR, and nu-SVR (default 1).
917   *
918   * @param value       the cost value
919   */
920  public void setCost(double value) {
921    m_Cost = value;
922  }
923 
924  /**
925   * Sets the parameter C of C-SVC, epsilon-SVR, and nu-SVR.
926   *
927   * @return            the cost value
928   */
929  public double getCost() {
930    return m_Cost;
931  }
932 
933  /**
934   * Returns the tip text for this property.
935   *
936   * @return tip text for this property suitable for
937   *         displaying in the explorer/experimenter gui
938   */
939  public String costTipText() {
940    return "The cost parameter C for C-SVC, epsilon-SVR and nu-SVR.";
941  }
942 
943  /**
944   * Sets tolerance of termination criterion (default 0.001).
945   *
946   * @param value       the tolerance
947   */
948  public void setEps(double value) {
949    m_eps = value;
950  }
951 
952  /**
953   * Gets tolerance of termination criterion.
954   *
955   * @return            the current tolerance
956   */
957  public double getEps() {
958    return m_eps;
959  }
960 
961  /**
962   * Returns the tip text for this property.
963   *
964   * @return tip text for this property suitable for
965   *         displaying in the explorer/experimenter gui
966   */
967  public String epsTipText() {
968    return "The tolerance of the termination criterion.";
969  }
970 
971  /**
972   * Sets the epsilon in loss function of epsilon-SVR (default 0.1).
973   *
974   * @param value       the loss epsilon
975   */
976  public void setLoss(double value) {
977    m_Loss = value;
978  }
979 
980  /**
981   * Gets the epsilon in loss function of epsilon-SVR.
982   *
983   * @return            the loss epsilon
984   */
985  public double getLoss() {
986    return m_Loss;
987  }
988 
989  /**
990   * Returns the tip text for this property.
991   *
992   * @return tip text for this property suitable for
993   *         displaying in the explorer/experimenter gui
994   */
995  public String lossTipText() {
996    return "The epsilon for the loss function in epsilon-SVR.";
997  }
998 
999  /**
1000   * whether to use the shrinking heuristics.
1001   *
1002   * @param value       true uses shrinking
1003   */
1004  public void setShrinking(boolean value) {
1005    m_Shrinking = value;
1006  }
1007 
1008  /**
1009   * whether to use the shrinking heuristics.
1010   *
1011   * @return            true, if shrinking is used
1012   */
1013  public boolean getShrinking() {
1014    return m_Shrinking;
1015  }
1016 
1017  /**
1018   * Returns the tip text for this property.
1019   *
1020   * @return tip text for this property suitable for
1021   *         displaying in the explorer/experimenter gui
1022   */
1023  public String shrinkingTipText() {
1024    return "Whether to use the shrinking heuristic.";
1025  }
1026 
1027  /**
1028   * whether to normalize input data.
1029   *
1030   * @param value       whether to normalize the data
1031   */
1032  public void setNormalize(boolean value) {
1033    m_Normalize = value;
1034  }
1035 
1036  /**
1037   * whether to normalize input data.
1038   *
1039   * @return            true, if the data is normalized
1040   */
1041  public boolean getNormalize() {
1042    return m_Normalize;
1043  }
1044 
1045  /**
1046   * Returns the tip text for this property.
1047   *
1048   * @return tip text for this property suitable for
1049   *         displaying in the explorer/experimenter gui
1050   */
1051  public String normalizeTipText() {
1052    return "Whether to normalize the data.";
1053  }
1054   
1055  /**
1056   * Returns the tip text for this property.
1057   *
1058   * @return tip text for this property suitable for
1059   *         displaying in the explorer/experimenter gui
1060   */
1061  public String doNotReplaceMissingValuesTipText() {
1062    return "Whether to turn off automatic replacement of missing "
1063      + "values. WARNING: set to true only if the data does not "
1064      + "contain missing values.";
1065  }
1066 
1067  /**
1068   * Whether to turn off automatic replacement of missing values.
1069   * Set to true only if the data does not contain missing values.
1070   *
1071   * @param b true if automatic missing values replacement is
1072   * to be disabled.
1073   */
1074  public void setDoNotReplaceMissingValues(boolean b) {
1075    m_noReplaceMissingValues = b;
1076  }
1077 
1078  /**
1079   * Gets whether automatic replacement of missing values is
1080   * disabled.
1081   *
1082   * @return true if automatic replacement of missing values
1083   * is disabled.
1084   */
1085  public boolean getDoNotReplaceMissingValues() {
1086    return m_noReplaceMissingValues;
1087  }
1088 
1089  /**
1090   * Sets the parameters C of class i to weight[i]*C, for C-SVC (default 1).
1091   * Blank separated list of doubles.
1092   *
1093   * @param weightsStr          the weights (doubles, separated by blanks)
1094   */
1095  public void setWeights(String weightsStr) {
1096    StringTokenizer       tok;
1097    int                   i;
1098   
1099    tok           = new StringTokenizer(weightsStr, " ");
1100    m_Weight      = new double[tok.countTokens()];
1101    m_WeightLabel = new int[tok.countTokens()];
1102   
1103    if (m_Weight.length == 0)
1104      System.out.println(
1105          "Zero Weights processed. Default weights will be used");
1106   
1107    for (i = 0; i < m_Weight.length; i++) {
1108      m_Weight[i]      = Double.parseDouble(tok.nextToken());
1109      m_WeightLabel[i] = i;
1110    }
1111  }
1112 
1113  /**
1114   * Gets the parameters C of class i to weight[i]*C, for C-SVC (default 1).
1115   * Blank separated doubles.
1116   *
1117   * @return            the weights (doubles separated by blanks)
1118   */
1119  public String getWeights() {
1120    String      result;
1121    int         i;
1122   
1123    result = "";
1124    for (i = 0; i < m_Weight.length; i++) {
1125      if (i > 0)
1126        result += " ";
1127      result += Double.toString(m_Weight[i]);
1128    }
1129   
1130    return result;
1131  }
1132 
1133  /**
1134   * Returns the tip text for this property.
1135   *
1136   * @return tip text for this property suitable for
1137   *         displaying in the explorer/experimenter gui
1138   */
1139  public String weightsTipText() {
1140    return "The weights to use for the classes (blank-separated list, eg, \"1 1 1\" for a 3-class problem), if empty 1 is used by default.";
1141  }
1142 
1143  /**
1144   * Sets whether probability estimates are generated instead of -1/+1 for
1145   * classification problems.
1146   *
1147   * @param value       whether to predict probabilities
1148   */
1149  public void setProbabilityEstimates(boolean value) {
1150    m_ProbabilityEstimates = value;
1151  }
1152 
1153  /**
1154   * Returns whether to generate probability estimates instead of -1/+1 for
1155   * classification problems.
1156   *
1157   * @return            true, if probability estimates should be returned
1158   */
1159  public boolean getProbabilityEstimates() {
1160    return m_ProbabilityEstimates;
1161  }
1162 
1163  /**
1164   * Returns the tip text for this property.
1165   *
1166   * @return tip text for this property suitable for
1167   *         displaying in the explorer/experimenter gui
1168   */
1169  public String probabilityEstimatesTipText() {
1170    return "Whether to generate probability estimates instead of -1/+1 for classification problems.";
1171  }
1172 
1173  /**
1174   * Sets the file to save the libsvm-internal model to. No model is saved if
1175   * pointing to a directory.
1176   *
1177   * @param value       the filename/directory
1178   */
1179  public void setModelFile(File value) {
1180    if (value == null)
1181      m_ModelFile = new File(System.getProperty("user.dir"));
1182    else
1183      m_ModelFile = value;
1184  }
1185 
1186  /**
1187   * Returns the file to save the libsvm-internal model to. No model is saved
1188   * if pointing to a directory.
1189   *
1190   * @return            the file object
1191   */
1192  public File getModelFile() {
1193    return m_ModelFile;
1194  }
1195 
1196  /**
1197   * Returns the tip text for this property.
1198   *
1199   * @return            tip text for this property suitable for
1200   *                    displaying in the explorer/experimenter gui
1201   */
1202  public String modelFileTipText() {
1203    return "The file to save the libsvm-internal model to; no model is saved if pointing to a directory.";
1204  }
1205 
1206  /**
1207   * sets the specified field.
1208   *
1209   * @param o           the object to set the field for
1210   * @param name        the name of the field
1211   * @param value       the new value of the field
1212   */
1213  protected void setField(Object o, String name, Object value) {
1214    Field       f;
1215   
1216    try {
1217      f = o.getClass().getField(name);
1218      f.set(o, value);
1219    }
1220    catch (Exception e) {
1221      e.printStackTrace();
1222    }
1223  }
1224 
1225  /**
1226   * sets the specified field in an array.
1227   *
1228   * @param o           the object to set the field for
1229   * @param name        the name of the field
1230   * @param index       the index in the array
1231   * @param value       the new value of the field
1232   */
1233  protected void setField(Object o, String name, int index, Object value) {
1234    Field       f;
1235   
1236    try {
1237      f = o.getClass().getField(name);
1238      Array.set(f.get(o), index, value);
1239    }
1240    catch (Exception e) {
1241      e.printStackTrace();
1242    }
1243  }
1244 
1245  /**
1246   * returns the current value of the specified field.
1247   *
1248   * @param o           the object the field is member of
1249   * @param name        the name of the field
1250   * @return            the value
1251   */
1252  protected Object getField(Object o, String name) {
1253    Field       f;
1254    Object      result;
1255   
1256    try {
1257      f      = o.getClass().getField(name);
1258      result = f.get(o);
1259    }
1260    catch (Exception e) {
1261      e.printStackTrace();
1262      result = null;
1263    }
1264   
1265    return result;
1266  }
1267 
1268  /**
1269   * sets a new array for the field.
1270   *
1271   * @param o           the object to set the array for
1272   * @param name        the name of the field
1273   * @param type        the type of the array
1274   * @param length      the length of the one-dimensional array
1275   */
1276  protected void newArray(Object o, String name, Class type, int length) {
1277    newArray(o, name, type, new int[]{length});
1278  }
1279 
1280  /**
1281   * sets a new array for the field.
1282   *
1283   * @param o           the object to set the array for
1284   * @param name        the name of the field
1285   * @param type        the type of the array
1286   * @param dimensions  the dimensions of the array
1287   */
1288  protected void newArray(Object o, String name, Class type, int[] dimensions) {
1289    Field       f;
1290   
1291    try {
1292      f = o.getClass().getField(name);
1293      f.set(o, Array.newInstance(type, dimensions));
1294    }
1295    catch (Exception e) {
1296      e.printStackTrace();
1297    }
1298  }
1299 
1300  /**
1301   * executes the specified method and returns the result, if any.
1302   *
1303   * @param o                   the object the method should be called from
1304   * @param name                the name of the method
1305   * @param paramClasses        the classes of the parameters
1306   * @param paramValues         the values of the parameters
1307   * @return                    the return value of the method, if any (in that case null)
1308   */
1309  protected Object invokeMethod(Object o, String name, Class[] paramClasses, Object[] paramValues) {
1310    Method      m;
1311    Object      result;
1312   
1313    result = null;
1314   
1315    try {
1316      m      = o.getClass().getMethod(name, paramClasses);
1317      result = m.invoke(o, paramValues);
1318    }
1319    catch (Exception e) {
1320      e.printStackTrace();
1321      result = null;
1322    }
1323   
1324    return result;
1325  }
1326 
1327  /**
1328   * transfers the local variables into a svm_parameter object.
1329   *
1330   * @return the configured svm_parameter object
1331   */
1332  protected Object getParameters() {
1333    Object      result;
1334    int         i;
1335   
1336    try {
1337      result = Class.forName(CLASS_SVMPARAMETER).newInstance();
1338     
1339      setField(result, "svm_type", new Integer(m_SVMType));
1340      setField(result, "kernel_type", new Integer(m_KernelType));
1341      setField(result, "degree", new Integer(m_Degree));
1342      setField(result, "gamma", new Double(m_GammaActual));
1343      setField(result, "coef0", new Double(m_Coef0));
1344      setField(result, "nu", new Double(m_nu));
1345      setField(result, "cache_size", new Double(m_CacheSize));
1346      setField(result, "C", new Double(m_Cost));
1347      setField(result, "eps", new Double(m_eps));
1348      setField(result, "p", new Double(m_Loss));
1349      setField(result, "shrinking", new Integer(m_Shrinking ? 1 : 0));
1350      setField(result, "nr_weight", new Integer(m_Weight.length));
1351      setField(result, "probability", new Integer(m_ProbabilityEstimates ? 1 : 0));
1352     
1353      newArray(result, "weight", Double.TYPE, m_Weight.length);
1354      newArray(result, "weight_label", Integer.TYPE, m_Weight.length);
1355      for (i = 0; i < m_Weight.length; i++) {
1356        setField(result, "weight", i, new Double(m_Weight[i]));
1357        setField(result, "weight_label", i, new Integer(m_WeightLabel[i]));
1358      }
1359    }
1360    catch (Exception e) {
1361      e.printStackTrace();
1362      result = null;
1363    }
1364   
1365    return result;
1366  }
1367 
1368  /**
1369   * returns the svm_problem.
1370   *
1371   * @param vx the x values
1372   * @param vy the y values
1373   * @return the svm_problem object
1374   */
1375  protected Object getProblem(Vector vx, Vector vy) {
1376    Object      result;
1377   
1378    try {
1379      result = Class.forName(CLASS_SVMPROBLEM).newInstance();
1380     
1381      setField(result, "l", new Integer(vy.size()));
1382     
1383      newArray(result, "x", Class.forName(CLASS_SVMNODE), new int[]{vy.size(), 0});
1384      for (int i = 0; i < vy.size(); i++)
1385        setField(result, "x", i, vx.elementAt(i));
1386     
1387      newArray(result, "y", Double.TYPE, vy.size());
1388      for (int i = 0; i < vy.size(); i++)
1389        setField(result, "y", i, vy.elementAt(i));
1390    }
1391    catch (Exception e) {
1392      e.printStackTrace();
1393      result = null;
1394    }
1395   
1396    return result;
1397  }
1398 
1399  /**
1400   * returns an instance into a sparse libsvm array.
1401   *
1402   * @param instance    the instance to work on
1403   * @return            the libsvm array
1404   * @throws Exception  if setup of array fails
1405   */
1406  protected Object instanceToArray(Instance instance) throws Exception {
1407    int         index;
1408    int         count;
1409    int         i;
1410    Object      result;
1411   
1412    // determine number of non-zero attributes
1413    /*for (i = 0; i < instance.numAttributes(); i++) {
1414      if (i == instance.classIndex())
1415        continue;
1416      if (instance.value(i) != 0)
1417        count++;
1418    } */
1419    count = 0;
1420    for (i = 0; i < instance.numValues(); i++) {
1421      if (instance.index(i) == instance.classIndex())
1422        continue;
1423      if (instance.valueSparse(i) != 0)
1424        count++;
1425    }
1426
1427    // fill array
1428    /* result = Array.newInstance(Class.forName(CLASS_SVMNODE), count);
1429    index  = 0;
1430    for (i = 0; i < instance.numAttributes(); i++) {
1431      if (i == instance.classIndex())
1432        continue;
1433      if (instance.value(i) == 0)
1434        continue;
1435
1436      Array.set(result, index, Class.forName(CLASS_SVMNODE).newInstance());
1437      setField(Array.get(result, index), "index", new Integer(i + 1));
1438      setField(Array.get(result, index), "value", new Double(instance.value(i)));
1439      index++;
1440    } */
1441   
1442    result = Array.newInstance(Class.forName(CLASS_SVMNODE), count);
1443    index  = 0;
1444    for (i = 0; i < instance.numValues(); i++) {
1445     
1446      int idx = instance.index(i);
1447      if (idx == instance.classIndex())
1448        continue;
1449      if (instance.valueSparse(i) == 0)
1450        continue;
1451
1452      Array.set(result, index, Class.forName(CLASS_SVMNODE).newInstance());
1453      setField(Array.get(result, index), "index", new Integer(idx + 1));
1454      setField(Array.get(result, index), "value", new Double(instance.valueSparse(i)));
1455      index++;
1456    }
1457   
1458    return result;
1459  }
1460 
1461  /**
1462   * Computes the distribution for a given instance.
1463   * In case of 1-class classification, 1 is returned at index 0 if libsvm
1464   * returns 1 and NaN (= missing) if libsvm returns -1.
1465   *
1466   * @param instance            the instance for which distribution is computed
1467   * @return                    the distribution
1468   * @throws Exception          if the distribution can't be computed successfully
1469   */
1470  public double[] distributionForInstance (Instance instance) throws Exception {       
1471    int[] labels = new int[instance.numClasses()];
1472    double[] prob_estimates = null;
1473
1474    if (m_ProbabilityEstimates) {
1475      invokeMethod(
1476          Class.forName(CLASS_SVM).newInstance(),
1477          "svm_get_labels",
1478          new Class[]{
1479            Class.forName(CLASS_SVMMODEL), 
1480            Array.newInstance(Integer.TYPE, instance.numClasses()).getClass()},
1481            new Object[]{
1482            m_Model, 
1483            labels});
1484
1485      prob_estimates = new double[instance.numClasses()];
1486    }
1487   
1488    if (!getDoNotReplaceMissingValues()) {
1489      m_ReplaceMissingValues.input(instance);
1490      m_ReplaceMissingValues.batchFinished();
1491      instance = m_ReplaceMissingValues.output();
1492    }
1493   
1494    if (m_Filter != null) {
1495      m_Filter.input(instance);
1496      m_Filter.batchFinished();
1497      instance = m_Filter.output();
1498    }
1499
1500    Object x = instanceToArray(instance);
1501    double v;
1502    double[] result = new double[instance.numClasses()];
1503    if (    m_ProbabilityEstimates
1504         && ((m_SVMType == SVMTYPE_C_SVC) || (m_SVMType == SVMTYPE_NU_SVC)) ) {
1505      v = ((Double) invokeMethod(
1506          Class.forName(CLASS_SVM).newInstance(),
1507          "svm_predict_probability",
1508          new Class[]{
1509            Class.forName(CLASS_SVMMODEL), 
1510            Array.newInstance(Class.forName(CLASS_SVMNODE), Array.getLength(x)).getClass(),
1511            Array.newInstance(Double.TYPE, prob_estimates.length).getClass()},
1512          new Object[]{
1513            m_Model, 
1514            x,
1515            prob_estimates})).doubleValue();
1516
1517      // Return order of probabilities to canonical weka attribute order
1518      for (int k = 0; k < prob_estimates.length; k++) {
1519        result[labels[k]] = prob_estimates[k];
1520      }
1521    }
1522    else {
1523      v = ((Double) invokeMethod(
1524          Class.forName(CLASS_SVM).newInstance(),
1525          "svm_predict",
1526          new Class[]{
1527            Class.forName(CLASS_SVMMODEL), 
1528            Array.newInstance(Class.forName(CLASS_SVMNODE), Array.getLength(x)).getClass()},
1529          new Object[]{
1530            m_Model, 
1531            x})).doubleValue();
1532     
1533      if (instance.classAttribute().isNominal()) {
1534        if (m_SVMType == SVMTYPE_ONE_CLASS_SVM) {
1535          if (v > 0)
1536            result[0] = 1;
1537          else
1538            result[0] = Double.NaN;  // outlier
1539        }
1540        else {
1541          result[(int) v] = 1;
1542        }
1543      }
1544      else {
1545        result[0] = v;
1546      }
1547    }
1548
1549    return result;               
1550  }
1551
1552  /**
1553   * Returns default capabilities of the classifier.
1554   *
1555   * @return      the capabilities of this classifier
1556   */
1557  public Capabilities getCapabilities() {
1558    Capabilities result = super.getCapabilities();
1559    result.disableAll();
1560
1561    // attributes
1562    result.enable(Capability.NOMINAL_ATTRIBUTES);
1563    result.enable(Capability.NUMERIC_ATTRIBUTES);
1564    result.enable(Capability.DATE_ATTRIBUTES);
1565
1566    // class
1567    result.enableDependency(Capability.UNARY_CLASS);
1568    result.enableDependency(Capability.NOMINAL_CLASS);
1569    result.enableDependency(Capability.NUMERIC_CLASS);
1570    result.enableDependency(Capability.DATE_CLASS);
1571
1572    switch (m_SVMType) {
1573      case SVMTYPE_C_SVC:
1574      case SVMTYPE_NU_SVC:
1575        result.enable(Capability.NOMINAL_CLASS);
1576        break;
1577       
1578      case SVMTYPE_ONE_CLASS_SVM:
1579        result.enable(Capability.UNARY_CLASS);
1580        break;
1581       
1582      case SVMTYPE_EPSILON_SVR:
1583      case SVMTYPE_NU_SVR:
1584        result.enable(Capability.NUMERIC_CLASS);
1585        result.enable(Capability.DATE_CLASS);
1586        break;
1587       
1588      default:
1589        throw new IllegalArgumentException("SVMType " + m_SVMType + " is not supported!");
1590    }
1591    result.enable(Capability.MISSING_CLASS_VALUES);
1592   
1593    return result;
1594  }
1595 
1596  /**
1597   * builds the classifier.
1598   *
1599   * @param insts       the training instances
1600   * @throws Exception  if libsvm classes not in classpath or libsvm
1601   *                    encountered a problem
1602   */
1603  public void buildClassifier(Instances insts) throws Exception {
1604    m_Filter = null;
1605   
1606    if (!isPresent())
1607      throw new Exception("libsvm classes not in CLASSPATH!");
1608
1609    // remove instances with missing class
1610    insts = new Instances(insts);
1611    insts.deleteWithMissingClass();
1612   
1613    if (!getDoNotReplaceMissingValues()) {
1614      m_ReplaceMissingValues = new ReplaceMissingValues();
1615      m_ReplaceMissingValues.setInputFormat(insts);
1616      insts = Filter.useFilter(insts, m_ReplaceMissingValues);
1617    }
1618   
1619    // can classifier handle the data?
1620    // we check this here so that if the user turns off
1621    // replace missing values filtering, it will fail
1622    // if the data actually does have missing values
1623    getCapabilities().testWithFail(insts);
1624       
1625    if (getNormalize()) {
1626      m_Filter = new Normalize();
1627      m_Filter.setInputFormat(insts);
1628      insts = Filter.useFilter(insts, m_Filter);
1629    }
1630   
1631    Vector vy = new Vector();
1632    Vector vx = new Vector();
1633    int max_index = 0;
1634   
1635    for (int d = 0; d < insts.numInstances(); d++) {
1636      Instance inst = insts.instance(d);
1637      Object x = instanceToArray(inst);
1638      int m = Array.getLength(x);
1639     
1640      if (m > 0)
1641        max_index = Math.max(max_index, ((Integer) getField(Array.get(x, m - 1), "index")).intValue());
1642      vx.addElement(x);
1643      vy.addElement(new Double(inst.classValue()));
1644    }
1645   
1646    // calculate actual gamma
1647    if (getGamma() == 0)
1648      m_GammaActual = 1.0 / max_index;
1649    else
1650      m_GammaActual = m_Gamma;
1651
1652    // check parameter
1653    String error_msg = (String) invokeMethod(
1654        Class.forName(CLASS_SVM).newInstance(), 
1655        "svm_check_parameter", 
1656        new Class[]{
1657          Class.forName(CLASS_SVMPROBLEM), 
1658          Class.forName(CLASS_SVMPARAMETER)},
1659        new Object[]{
1660          getProblem(vx, vy), 
1661          getParameters()});
1662   
1663    if (error_msg != null)
1664      throw new Exception("Error: " + error_msg);
1665   
1666    // train model
1667    m_Model = invokeMethod(
1668        Class.forName(CLASS_SVM).newInstance(), 
1669        "svm_train", 
1670        new Class[]{
1671          Class.forName(CLASS_SVMPROBLEM), 
1672          Class.forName(CLASS_SVMPARAMETER)},
1673        new Object[]{
1674          getProblem(vx, vy), 
1675          getParameters()});
1676   
1677    // save internal model?
1678    if (!m_ModelFile.isDirectory()) {
1679      invokeMethod(
1680          Class.forName(CLASS_SVM).newInstance(), 
1681          "svm_save_model", 
1682          new Class[]{
1683            String.class, 
1684            Class.forName(CLASS_SVMMODEL)},
1685            new Object[]{
1686            m_ModelFile.getAbsolutePath(), 
1687            m_Model});
1688    }
1689  }
1690   
1691  /**
1692   * returns a string representation.
1693   *
1694   * @return            a string representation
1695   */
1696  public String toString() {
1697    return "LibSVM wrapper, original code by Yasser EL-Manzalawy (= WLSVM)";
1698  }
1699 
1700  /**
1701   * Returns the revision string.
1702   *
1703   * @return            the revision
1704   */
1705  public String getRevision() {
1706    return RevisionUtils.extract("$Revision: 5928 $");
1707  }
1708 
1709  /**
1710   * Main method for testing this class.
1711   *
1712   * @param args the options
1713   */
1714  public static void main(String[] args) {
1715    runClassifier(new LibSVM(), args);
1716  }
1717}
Note: See TracBrowser for help on using the repository browser.