/* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* * CheckSource.java * Copyright (C) 2007-2012 University of Waikato, Hamilton, New Zealand */ package weka.filters; import java.io.File; import java.util.Enumeration; import java.util.Vector; import weka.core.Instance; import weka.core.Instances; import weka.core.Option; import weka.core.OptionHandler; import weka.core.RevisionHandler; import weka.core.RevisionUtils; import weka.core.Utils; import weka.core.converters.ConverterUtils.DataSource; /** * A simple class for checking the source generated from Filters * implementing the <code>weka.filters.Sourcable</code> interface. * It takes a filter, the classname of the generated source * and the dataset the source was generated with as parameters and tests * the output of the built filter against the output of the generated * source. Use option '-h' to display all available commandline options. * <!-- options-start --> * Valid options are: <p/> * * <pre> -W <classname and options> * The filter (incl. options) that was used to generate * the source code.</pre> * * <pre> -S <classname> * The classname of the generated source code.</pre> * * <pre> -t <file> * The training set with which the source code was generated.</pre> * * <pre> -c <index> * The class index of the training set. 'first' and 'last' are * valid indices. * (default: none)</pre> * <!-- options-end --> * * Options after -- are passed to the designated filter.<p> * * @author fracpete (fracpete at waikato dot ac dot nz) * @version $Revision: 8034 $ * @see weka.filters.Sourcable */ public class CheckSource implements OptionHandler, RevisionHandler { /** the classifier used for generating the source code */ protected Filter m_Filter = null; /** the generated source code */ protected Filter m_SourceCode = null; /** the dataset to use for testing */ protected File m_Dataset = null; /** the class index */ protected int m_ClassIndex = -1; /** * Returns an enumeration describing the available options. * * @return an enumeration of all the available options. */ public Enumeration listOptions() { Vector result = new Vector(); result.addElement(new Option( "\tThe filter (incl. options) that was used to generate\n" + "\tthe source code.", "W", 1, "-W <classname and options>")); result.addElement(new Option( "\tThe classname of the generated source code.", "S", 1, "-S <classname>")); result.addElement(new Option( "\tThe training set with which the source code was generated.", "t", 1, "-t <file>")); result.addElement(new Option( "\tThe class index of the training set. 'first' and 'last' are\n" + "\tvalid indices.\n" + "\t(default: none)", "c", 1, "-c <index>")); return result.elements(); } /** * Parses a given list of options. <p/> * <!-- options-start --> * Valid options are: <p/> * * <pre> -W <classname and options> * The filter (incl. options) that was used to generate * the source code.</pre> * * <pre> -S <classname> * The classname of the generated source code.</pre> * * <pre> -t <file> * The training set with which the source code was generated.</pre> * * <pre> -c <index> * The class index of the training set. 'first' and 'last' are * valid indices. * (default: none)</pre> * <!-- options-end --> * * Options after -- are passed to the designated filter.<p> * * @param options the list of options as an array of strings * @throws Exception if an option is not supported */ public void setOptions(String[] options) throws Exception { String tmpStr; String[] spec; String classname; tmpStr = Utils.getOption('W', options); if (tmpStr.length() > 0) { spec = Utils.splitOptions(tmpStr); if (spec.length == 0) throw new IllegalArgumentException("Invalid filter specification string"); classname = spec[0]; spec[0] = ""; setFilter((Filter) Utils.forName(Filter.class, classname, spec)); } else { throw new Exception("No filter (classname + options) provided!"); } tmpStr = Utils.getOption('S', options); if (tmpStr.length() > 0) { spec = Utils.splitOptions(tmpStr); if (spec.length != 1) throw new IllegalArgumentException("Invalid source code specification string"); classname = spec[0]; spec[0] = ""; setSourceCode((Filter) Utils.forName(Filter.class, classname, spec)); } else { throw new Exception("No source code (classname) provided!"); } tmpStr = Utils.getOption('t', options); if (tmpStr.length() != 0) setDataset(new File(tmpStr)); else throw new Exception("No dataset provided!"); tmpStr = Utils.getOption('c', options); if (tmpStr.length() != 0) { if (tmpStr.equals("first")) setClassIndex(0); else if (tmpStr.equals("last")) setClassIndex(-2); else setClassIndex(Integer.parseInt(tmpStr) - 1); } else { setClassIndex(-1); } } /** * Gets the current settings of the filter. * * @return an array of strings suitable for passing to setOptions */ public String[] getOptions() { Vector<String> result; result = new Vector<String>(); if (getFilter() != null) { result.add("-W"); result.add(getFilter().getClass().getName() + " " + Utils.joinOptions(((OptionHandler) getFilter()).getOptions())); } if (getSourceCode() != null) { result.add("-S"); result.add(getSourceCode().getClass().getName()); } if (getDataset() != null) { result.add("-t"); result.add(m_Dataset.getAbsolutePath()); } if (getClassIndex() != -1) { result.add("-c"); if (getClassIndex() == -2) result.add("last"); else if (getClassIndex() == 0) result.add("first"); else result.add("" + (getClassIndex() + 1)); } return result.toArray(new String[result.size()]); } /** * Sets the filter to use for the comparison. * * @param value the filter to use */ public void setFilter(Filter value) { m_Filter = value; } /** * Gets the filter being used for the tests, can be null. * * @return the currently set filter */ public Filter getFilter() { return m_Filter; } /** * Sets the class to test. * * @param value the class to test */ public void setSourceCode(Filter value) { m_SourceCode = value; } /** * Gets the class to test. * * @return the currently set class, can be null. */ public Filter getSourceCode() { return m_SourceCode; } /** * Sets the dataset to use for testing. * * @param value the dataset to use. */ public void setDataset(File value) { if (!value.exists()) throw new IllegalArgumentException( "Dataset '" + value.getAbsolutePath() + "' does not exist!"); else m_Dataset = value; } /** * Gets the dataset to use for testing, can be null. * * @return the dataset to use. */ public File getDataset() { return m_Dataset; } /** * Sets the class index of the dataset. * * @param value the class index of the dataset. */ public void setClassIndex(int value) { m_ClassIndex = value; } /** * Gets the class index of the dataset. * * @return the current class index. */ public int getClassIndex() { return m_ClassIndex; } /** * compares two Instance * * @param inst1 the first Instance object to compare * @param inst2 the second Instance object to compare * @return true if both are the same */ protected boolean compare(Instance inst1, Instance inst2) { boolean result; int i; // check dimension result = (inst1.numAttributes() == inst2.numAttributes()); // check content if (result) { for (i = 0; i < inst1.numAttributes(); i++) { if (Double.isNaN(inst1.value(i)) && (Double.isNaN(inst2.value(i)))) continue; if (inst1.value(i) != inst2.value(i)) { result = false; System.out.println( "Values at position " + (i+1) + " differ (Filter/Source code): " + inst1.value(i) + " != " + inst2.value(i)); break; } } } return result; } /** * compares the two Instances objects * * @param inst1 the first Instances object to compare * @param inst2 the second Instances object to compare * @return true if both are the same */ protected boolean compare(Instances inst1, Instances inst2) { boolean result; int i; // check dimensions result = (inst1.numInstances() == inst2.numInstances()); // check content if (result) { for (i = 0; i < inst1.numInstances(); i++) { result = compare(inst1.instance(i), inst2.instance(i)); if (!result) { System.out.println( "Values in line " + (i+1) + " differ!"); break; } } } return result; } /** * performs the comparison test * * @return true if tests were successful * @throws Exception if tests fail */ public boolean execute() throws Exception { boolean result; Instances data; Instance filteredInstance; Instances filteredInstances; Instance filteredInstanceSource; Instances filteredInstancesSource; DataSource source; Filter filter; Filter filterSource; int i; result = true; // a few checks if (getFilter() == null) throw new Exception("No filter set!"); if (getSourceCode() == null) throw new Exception("No source code set!"); if (getDataset() == null) throw new Exception("No dataset set!"); if (!getDataset().exists()) throw new Exception( "Dataset '" + getDataset().getAbsolutePath() + "' does not exist!"); // load data source = new DataSource(getDataset().getAbsolutePath()); data = source.getDataSet(); if (getClassIndex() == -2) data.setClassIndex(data.numAttributes() - 1); else data.setClassIndex(getClassIndex()); // compare output // 1. batch filtering filter = Filter.makeCopy(getFilter()); filter.setInputFormat(data); filteredInstances = Filter.useFilter(data, filter); filterSource = Filter.makeCopy(getSourceCode()); filterSource.setInputFormat(data); filteredInstancesSource = Filter.useFilter(data, filterSource); result = compare(filteredInstances, filteredInstancesSource); // 2. instance by instance if (result) { filter = Filter.makeCopy(getFilter()); filter.setInputFormat(data); Filter.useFilter(data, filter); filterSource = Filter.makeCopy(getSourceCode()); filterSource.setInputFormat(data); for (i = 0; i < data.numInstances(); i++) { filter.input(data.instance(i)); filter.batchFinished(); filteredInstance = filter.output(); filterSource.input(data.instance(i)); filterSource.batchFinished(); filteredInstanceSource = filterSource.output(); if (!compare(filteredInstance, filteredInstanceSource)) System.out.println( (i+1) + ". instance (Filter/Source code): " + filteredInstance + " != " + filteredInstanceSource); } } return result; } /** * Returns the revision string. * * @return the revision */ public String getRevision() { return RevisionUtils.extract("$Revision: 8034 $"); } /** * Executes the tests, use "-h" to list the commandline options. * * @param args the commandline parameters * @throws Exception if something goes wrong */ public static void main(String[] args) throws Exception{ CheckSource check; StringBuffer text; Enumeration enm; check = new CheckSource(); if (Utils.getFlag('h', args)) { text = new StringBuffer(); text.append("\nHelp requested:\n\n"); enm = check.listOptions(); while (enm.hasMoreElements()) { Option option = (Option) enm.nextElement(); text.append(option.synopsis() + "\n"); text.append(option.description() + "\n"); } System.out.println("\n" + text + "\n"); } else { check.setOptions(args); if (check.execute()) System.out.println("Tests OK!"); else System.out.println("Tests failed!"); } } }