/* * 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/>. */ /* * MexicanHat.java * Copyright (C) 2005-2012 University of Waikato, Hamilton, New Zealand * */ package weka.datagenerators.classifiers.regression; import java.util.Enumeration; import java.util.Random; import java.util.Vector; import weka.core.Attribute; import weka.core.DenseInstance; import weka.core.FastVector; import weka.core.Instance; import weka.core.Instances; import weka.core.Option; import weka.core.RevisionUtils; import weka.core.Utils; import weka.datagenerators.RegressionGenerator; /** <!-- globalinfo-start --> * A data generator for the simple 'Mexian Hat' function:<br/> * y = sin|x| / |x|<br/> * In addition to this simple function, the amplitude can be changed and gaussian noise can be added. * <p/> <!-- globalinfo-end --> * <!-- options-start --> * Valid options are: <p/> * * <pre> -h * Prints this help.</pre> * * <pre> -o <file> * The name of the output file, otherwise the generated data is * printed to stdout.</pre> * * <pre> -r <name> * The name of the relation.</pre> * * <pre> -d * Whether to print debug informations.</pre> * * <pre> -S * The seed for random function (default 1)</pre> * * <pre> -n <num> * The number of examples to generate (default 100)</pre> * * <pre> -A <num> * The amplitude multiplier (default 1.0).</pre> * * <pre> -R <num>..<num> * The range x is randomly drawn from (default -10.0..10.0).</pre> * * <pre> -N <num> * The noise rate (default 0.0).</pre> * * <pre> -V <num> * The noise variance (default 1.0).</pre> * <!-- options-end --> * * @author FracPete (fracpete at waikato dot ac dot nz) * @version $Revision: 8034 $ */ public class MexicanHat extends RegressionGenerator { /** for serialization */ static final long serialVersionUID = 4577016375261512975L; /** the amplitude of y */ protected double m_Amplitude; /** the lower boundary of the range, x is drawn from */ protected double m_MinRange; /** the upper boundary of the range, x is drawn from */ protected double m_MaxRange; /** the rate of the gaussian noise */ protected double m_NoiseRate; /** the variance of the gaussian noise */ protected double m_NoiseVariance; /** the random number generator for the noise */ protected Random m_NoiseRandom = null; /** * initializes the generator */ public MexicanHat() { super(); setAmplitude(defaultAmplitude()); setMinRange(defaultMinRange()); setMaxRange(defaultMaxRange()); setNoiseRate(defaultNoiseRate()); setNoiseVariance(defaultNoiseVariance()); } /** * Returns a string describing this data generator. * * @return a description of the data generator suitable for * displaying in the explorer/experimenter gui */ public String globalInfo() { return "A data generator for the simple 'Mexian Hat' function:\n" + " y = sin|x| / |x|\n" + "In addition to this simple function, the amplitude can be changed and " + "gaussian noise can be added."; } /** * Returns an enumeration describing the available options. * * @return an enumeration of all the available options */ public Enumeration listOptions() { Vector result = enumToVector(super.listOptions()); result.addElement(new Option( "\tThe amplitude multiplier (default " + defaultAmplitude() + ").", "A", 1, "-A <num>")); result.addElement(new Option( "\tThe range x is randomly drawn from (default " + defaultMinRange() + ".." + defaultMaxRange() + ").", "R", 1, "-R <num>..<num>")); result.addElement(new Option( "\tThe noise rate (default " + defaultNoiseRate() + ").", "N", 1, "-N <num>")); result.addElement(new Option( "\tThe noise variance (default " + defaultNoiseVariance() + ").", "V", 1, "-V <num>")); return result.elements(); } /** * Parses a list of options for this object. <p/> * <!-- options-start --> * Valid options are: <p/> * * <pre> -h * Prints this help.</pre> * * <pre> -o <file> * The name of the output file, otherwise the generated data is * printed to stdout.</pre> * * <pre> -r <name> * The name of the relation.</pre> * * <pre> -d * Whether to print debug informations.</pre> * * <pre> -S * The seed for random function (default 1)</pre> * * <pre> -n <num> * The number of examples to generate (default 100)</pre> * * <pre> -A <num> * The amplitude multiplier (default 1.0).</pre> * * <pre> -R <num>..<num> * The range x is randomly drawn from (default -10.0..10.0).</pre> * * <pre> -N <num> * The noise rate (default 0.0).</pre> * * <pre> -V <num> * The noise variance (default 1.0).</pre> * <!-- options-end --> * * @param options the list of options as an array of strings * @exception Exception if an option is not supported */ public void setOptions(String[] options) throws Exception { String tmpStr; super.setOptions(options); tmpStr = Utils.getOption('A', options); if (tmpStr.length() != 0) setAmplitude(Double.parseDouble(tmpStr)); else setAmplitude(defaultAmplitude()); tmpStr = Utils.getOption('R', options); if (tmpStr.length() != 0) setRange(tmpStr); else setRange(defaultMinRange() + ".." + defaultMaxRange()); tmpStr = Utils.getOption('N', options); if (tmpStr.length() != 0) setNoiseRate(Double.parseDouble(tmpStr)); else setNoiseRate(defaultNoiseRate()); tmpStr = Utils.getOption('V', options); if (tmpStr.length() != 0) setNoiseVariance(Double.parseDouble(tmpStr)); else setNoiseVariance(defaultNoiseVariance()); } /** * Gets the current settings of the datagenerator BIRCHCluster. * * @return an array of strings suitable for passing to setOptions */ public String[] getOptions() { Vector result; String[] options; int i; result = new Vector(); options = removeBlacklist(super.getOptions()); for (i = 0; i < options.length; i++) result.add(options[i]); result.add("-A"); result.add("" + getAmplitude()); result.add("-R"); result.add("" + getRange()); result.add("-N"); result.add("" + getNoiseRate()); result.add("-V"); result.add("" + getNoiseVariance()); return (String[]) result.toArray(new String[result.size()]); } /** * returns the default amplitude * * @return the default amplitude */ protected double defaultAmplitude() { return 1.0; } /** * Gets the amplitude multiplier. * * @return the amplitude multiplier */ public double getAmplitude() { return m_Amplitude; } /** * Sets the amplitude multiplier. * * @param value the amplitude multiplier */ public void setAmplitude(double value) { m_Amplitude = value; } /** * Returns the tip text for this property * * @return tip text for this property suitable for * displaying in the explorer/experimenter gui */ public String amplitudeTipText() { return "The amplitude of the mexican hat."; } /** * Sets the upper and lower boundary for the range of x * * @param fromTo the string containing the upper and lower boundary for * the range of x, separated by .. */ protected void setRange(String fromTo) { int i = fromTo.indexOf(".."); String from = fromTo.substring(0, i); setMinRange(Double.valueOf(from).doubleValue()); String to = fromTo.substring(i + 2, fromTo.length()); setMaxRange(Double.valueOf(to).doubleValue()); } /** * Gets the upper and lower boundary for the range of x * * @return the string containing the upper and lower boundary for * the range of x, separated by .. */ protected String getRange() { String fromTo = "" + Utils.doubleToString(getMinRange(), 2) + ".." + Utils.doubleToString(getMaxRange(), 2); return fromTo; } /** * Returns the tip text for this property * * @return tip text for this property suitable for * displaying in the explorer/experimenter gui */ protected String rangeTipText() { return "The upper and lower boundary for the range x is drawn from randomly."; } /** * returns the default min range * * @return the default min range */ protected double defaultMinRange() { return -10; } /** * Sets the lower boundary for the range of x * * @param value the lower boundary */ public void setMinRange(double value) { m_MinRange = value; } /** * Gets the lower boundary for the range of x * * @return the lower boundary for the range of x */ public double getMinRange() { return m_MinRange; } /** * Returns the tip text for this property * * @return tip text for this property suitable for * displaying in the explorer/experimenter gui */ public String minRangeTipText() { return "The lower boundary for the range x is drawn from randomly."; } /** * returns the default max range * * @return the default max range */ protected double defaultMaxRange() { return 10; } /** * Sets the upper boundary for the range of x * * @param value the upper boundary */ public void setMaxRange(double value) { m_MaxRange = value; } /** * Gets the upper boundary for the range of x * * @return the upper boundary for the range of x */ public double getMaxRange() { return m_MaxRange; } /** * Returns the tip text for this property * * @return tip text for this property suitable for * displaying in the explorer/experimenter gui */ public String maxRangeTipText() { return "The upper boundary for the range x is drawn from randomly."; } /** * returns the default gaussian noise rate * * @return the default gaussian noise rate */ protected double defaultNoiseRate() { return 0.0; } /** * Gets the gaussian noise rate. * * @return the gaussian noise rate */ public double getNoiseRate() { return m_NoiseRate; } /** * Sets the gaussian noise rate. * * @param value the gaussian noise rate */ public void setNoiseRate(double value) { m_NoiseRate = value; } /** * Returns the tip text for this property * * @return tip text for this property suitable for * displaying in the explorer/experimenter gui */ public String noiseRateTipText() { return "The gaussian noise rate to use."; } /** * returns the default variance of the noise rate * * @return the default variance of the noise rate */ protected double defaultNoiseVariance() { return 1.0; } /** * Gets the noise variance * * @return the noise variance */ public double getNoiseVariance() { return m_NoiseVariance; } /** * Sets the noise variance * * @param value the noise variance */ public void setNoiseVariance(double value) { if (value > 0) m_NoiseVariance = value; else throw new IllegalArgumentException( "Noise variance needs to be > 0 (provided: " + value + ")!"); } /** * Returns the tip text for this property * * @return tip text for this property suitable for * displaying in the explorer/experimenter gui */ public String noiseVarianceTipText() { return "The noise variance to use."; } /** * Return if single mode is set for the given data generator * mode depends on option setting and or generator type. * * @return single mode flag * @throws Exception if mode is not set yet */ public boolean getSingleModeFlag() throws Exception { return true; } /** * Initializes the format for the dataset produced. * Must be called before the generateExample or generateExamples * methods are used. * Re-initializes the random number generator with the given seed. * * @return the format for the dataset * @throws Exception if the generating of the format failed * @see #getSeed() */ public Instances defineDataFormat() throws Exception { FastVector atts; m_Random = new Random(getSeed()); m_NoiseRandom = new Random(getSeed()); // number of examples is the same as given per option setNumExamplesAct(getNumExamples()); // initialize dataset format atts = new FastVector(); atts.addElement(new Attribute("x")); atts.addElement(new Attribute("y")); m_DatasetFormat = new Instances(getRelationNameToUse(), atts, 0); return m_DatasetFormat; } /** * Generates one example of the dataset. * * @return the generated example * @throws Exception if the format of the dataset is not yet defined * @throws Exception if the generator only works with generateExamples * which means in non single mode */ public Instance generateExample() throws Exception { Instance result; Random rand; double x; double y; double[] atts; result = null; rand = getRandom(); if (m_DatasetFormat == null) throw new Exception("Dataset format not defined."); // generate attributes atts = new double[m_DatasetFormat.numAttributes()]; // random x x = rand.nextDouble(); // fit into range x = x * (getMaxRange() - getMinRange()) + getMinRange(); // generate y if (Utils.eq(x, 0)) y = getAmplitude(); else y = getAmplitude() * StrictMath.sin(StrictMath.abs(x)) / StrictMath.abs(x); // noise y = y + getAmplitude() * m_NoiseRandom.nextGaussian() * getNoiseRate() * getNoiseVariance(); atts[0] = x; atts[1] = y; result = new DenseInstance(1.0, atts); // dataset reference result.setDataset(m_DatasetFormat); return result; } /** * Generates all examples of the dataset. Re-initializes the random number * generator with the given seed, before generating instances. * * @return the generated dataset * @throws Exception if the format of the dataset is not yet defined * @throws Exception if the generator only works with generateExample, * which means in single mode * @see #getSeed() */ public Instances generateExamples() throws Exception { Instances result; int i; result = new Instances(m_DatasetFormat, 0); m_Random = new Random(getSeed()); for (i = 0; i < getNumExamplesAct(); i++) result.add(generateExample()); return result; } /** * Generates a comment string that documentates the data generator. * By default this string is added at the beginning of the produced output * as ARFF file type, next after the options. * * @return string contains info about the generated rules */ public String generateStart () { return ""; } /** * Generates a comment string that documentats the data generator. * By default this string is added at the end of theproduces output * as ARFF file type. * * @return string contains info about the generated rules * @throws Exception if the generating of the documentaion fails */ public String generateFinished() throws Exception { return ""; } /** * Returns the revision string. * * @return the revision */ public String getRevision() { return RevisionUtils.extract("$Revision: 8034 $"); } /** * Main method for testing this class. * * @param args should contain arguments for the data producer: */ public static void main(String[] args) { runDataGenerator(new MexicanHat(), args); } }