diff options
Diffstat (limited to 'src/main/java/org/apache/commons/math/random/ValueServer.java')
-rw-r--r-- | src/main/java/org/apache/commons/math/random/ValueServer.java | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/math/random/ValueServer.java b/src/main/java/org/apache/commons/math/random/ValueServer.java new file mode 100644 index 0000000..9146e69 --- /dev/null +++ b/src/main/java/org/apache/commons/math/random/ValueServer.java @@ -0,0 +1,385 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math.random; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; + +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.exception.util.LocalizedFormats; + +/** + * Generates values for use in simulation applications. + * <p> + * How values are generated is determined by the <code>mode</code> + * property.</p> + * <p> + * Supported <code>mode</code> values are: <ul> + * <li> DIGEST_MODE -- uses an empirical distribution </li> + * <li> REPLAY_MODE -- replays data from <code>valuesFileURL</code></li> + * <li> UNIFORM_MODE -- generates uniformly distributed random values with + * mean = <code>mu</code> </li> + * <li> EXPONENTIAL_MODE -- generates exponentially distributed random values + * with mean = <code>mu</code></li> + * <li> GAUSSIAN_MODE -- generates Gaussian distributed random values with + * mean = <code>mu</code> and + * standard deviation = <code>sigma</code></li> + * <li> CONSTANT_MODE -- returns <code>mu</code> every time.</li></ul></p> + * + * @version $Revision: 1003886 $ $Date: 2010-10-02 23:04:44 +0200 (sam. 02 oct. 2010) $ + * + */ +public class ValueServer { + + /** Use empirical distribution. */ + public static final int DIGEST_MODE = 0; + + /** Replay data from valuesFilePath. */ + public static final int REPLAY_MODE = 1; + + /** Uniform random deviates with mean = μ. */ + public static final int UNIFORM_MODE = 2; + + /** Exponential random deviates with mean = μ. */ + public static final int EXPONENTIAL_MODE = 3; + + /** Gaussian random deviates with mean = μ, std dev = σ. */ + public static final int GAUSSIAN_MODE = 4; + + /** Always return mu */ + public static final int CONSTANT_MODE = 5; + + /** mode determines how values are generated. */ + private int mode = 5; + + /** URI to raw data values. */ + private URL valuesFileURL = null; + + /** Mean for use with non-data-driven modes. */ + private double mu = 0.0; + + /** Standard deviation for use with GAUSSIAN_MODE. */ + private double sigma = 0.0; + + /** Empirical probability distribution for use with DIGEST_MODE. */ + private EmpiricalDistribution empiricalDistribution = null; + + /** File pointer for REPLAY_MODE. */ + private BufferedReader filePointer = null; + + /** RandomDataImpl to use for random data generation. */ + private final RandomData randomData; + + // Data generation modes ====================================== + + /** Creates new ValueServer */ + public ValueServer() { + randomData = new RandomDataImpl(); + } + + /** + * Construct a ValueServer instance using a RandomData as its source + * of random data. + * + * @param randomData the RandomData instance used to source random data + * @since 1.1 + */ + public ValueServer(RandomData randomData) { + this.randomData = randomData; + } + + /** + * Returns the next generated value, generated according + * to the mode value (see MODE constants). + * + * @return generated value + * @throws IOException in REPLAY_MODE if a file I/O error occurs + */ + public double getNext() throws IOException { + switch (mode) { + case DIGEST_MODE: return getNextDigest(); + case REPLAY_MODE: return getNextReplay(); + case UNIFORM_MODE: return getNextUniform(); + case EXPONENTIAL_MODE: return getNextExponential(); + case GAUSSIAN_MODE: return getNextGaussian(); + case CONSTANT_MODE: return mu; + default: throw MathRuntimeException.createIllegalStateException( + LocalizedFormats.UNKNOWN_MODE, + mode, + "DIGEST_MODE", DIGEST_MODE, "REPLAY_MODE", REPLAY_MODE, + "UNIFORM_MODE", UNIFORM_MODE, "EXPONENTIAL_MODE", EXPONENTIAL_MODE, + "GAUSSIAN_MODE", GAUSSIAN_MODE, "CONSTANT_MODE", CONSTANT_MODE); + } + } + + /** + * Fills the input array with values generated using getNext() repeatedly. + * + * @param values array to be filled + * @throws IOException in REPLAY_MODE if a file I/O error occurs + */ + public void fill(double[] values) throws IOException { + for (int i = 0; i < values.length; i++) { + values[i] = getNext(); + } + } + + /** + * Returns an array of length <code>length</code> with values generated + * using getNext() repeatedly. + * + * @param length length of output array + * @return array of generated values + * @throws IOException in REPLAY_MODE if a file I/O error occurs + */ + public double[] fill(int length) throws IOException { + double[] out = new double[length]; + for (int i = 0; i < length; i++) { + out[i] = getNext(); + } + return out; + } + + /** + * Computes the empirical distribution using values from the file + * in <code>valuesFileURL</code>, using the default number of bins. + * <p> + * <code>valuesFileURL</code> must exist and be + * readable by *this at runtime.</p> + * <p> + * This method must be called before using <code>getNext()</code> + * with <code>mode = DIGEST_MODE</code></p> + * + * @throws IOException if an I/O error occurs reading the input file + */ + public void computeDistribution() throws IOException { + empiricalDistribution = new EmpiricalDistributionImpl(); + empiricalDistribution.load(valuesFileURL); + } + + /** + * Computes the empirical distribution using values from the file + * in <code>valuesFileURL</code> and <code>binCount</code> bins. + * <p> + * <code>valuesFileURL</code> must exist and be readable by this process + * at runtime.</p> + * <p> + * This method must be called before using <code>getNext()</code> + * with <code>mode = DIGEST_MODE</code></p> + * + * @param binCount the number of bins used in computing the empirical + * distribution + * @throws IOException if an error occurs reading the input file + */ + public void computeDistribution(int binCount) + throws IOException { + empiricalDistribution = new EmpiricalDistributionImpl(binCount); + empiricalDistribution.load(valuesFileURL); + mu = empiricalDistribution.getSampleStats().getMean(); + sigma = empiricalDistribution.getSampleStats().getStandardDeviation(); + } + + /** Getter for property mode. + * @return Value of property mode. + */ + public int getMode() { + return mode; + } + + /** Setter for property mode. + * @param mode New value of property mode. + */ + public void setMode(int mode) { + this.mode = mode; + } + + /** + * Getter for <code>valuesFileURL<code> + * @return Value of property valuesFileURL. + */ + public URL getValuesFileURL() { + return valuesFileURL; + } + + /** + * Sets the <code>valuesFileURL</code> using a string URL representation + * @param url String representation for new valuesFileURL. + * @throws MalformedURLException if url is not well formed + */ + public void setValuesFileURL(String url) throws MalformedURLException { + this.valuesFileURL = new URL(url); + } + + /** + * Sets the <code>valuesFileURL</code> + * @param url New value of property valuesFileURL. + */ + public void setValuesFileURL(URL url) { + this.valuesFileURL = url; + } + + /** Getter for property empiricalDistribution. + * @return Value of property empiricalDistribution. + */ + public EmpiricalDistribution getEmpiricalDistribution() { + return empiricalDistribution; + } + + /** + * Resets REPLAY_MODE file pointer to the beginning of the <code>valuesFileURL</code>. + * + * @throws IOException if an error occurs opening the file + */ + public void resetReplayFile() throws IOException { + if (filePointer != null) { + try { + filePointer.close(); + filePointer = null; + } catch (IOException ex) { + // ignore + } + } + filePointer = new BufferedReader(new InputStreamReader(valuesFileURL.openStream())); + } + + /** + * Closes <code>valuesFileURL</code> after use in REPLAY_MODE. + * + * @throws IOException if an error occurs closing the file + */ + public void closeReplayFile() throws IOException { + if (filePointer != null) { + filePointer.close(); + filePointer = null; + } + } + + /** Getter for property mu. + * @return Value of property mu. + */ + public double getMu() { + return mu; + } + + /** Setter for property mu. + * @param mu New value of property mu. + */ + public void setMu(double mu) { + this.mu = mu; + } + + /** Getter for property sigma. + * @return Value of property sigma. + */ + public double getSigma() { + return sigma; + } + + /** Setter for property sigma. + * @param sigma New value of property sigma. + */ + public void setSigma(double sigma) { + this.sigma = sigma; + } + + //------------- private methods --------------------------------- + + /** + * Gets a random value in DIGEST_MODE. + * <p> + * <strong>Preconditions</strong>: <ul> + * <li>Before this method is called, <code>computeDistribution()</code> + * must have completed successfully; otherwise an + * <code>IllegalStateException</code> will be thrown</li></ul></p> + * + * @return next random value from the empirical distribution digest + */ + private double getNextDigest() { + if ((empiricalDistribution == null) || + (empiricalDistribution.getBinStats().size() == 0)) { + throw MathRuntimeException.createIllegalStateException(LocalizedFormats.DIGEST_NOT_INITIALIZED); + } + return empiricalDistribution.getNextValue(); + } + + /** + * Gets next sequential value from the <code>valuesFileURL</code>. + * <p> + * Throws an IOException if the read fails.</p> + * <p> + * This method will open the <code>valuesFileURL</code> if there is no + * replay file open.</p> + * <p> + * The <code>valuesFileURL</code> will be closed and reopened to wrap around + * from EOF to BOF if EOF is encountered. EOFException (which is a kind of + * IOException) may still be thrown if the <code>valuesFileURL</code> is + * empty.</p> + * + * @return next value from the replay file + * @throws IOException if there is a problem reading from the file + * @throws NumberFormatException if an invalid numeric string is + * encountered in the file + */ + private double getNextReplay() throws IOException { + String str = null; + if (filePointer == null) { + resetReplayFile(); + } + if ((str = filePointer.readLine()) == null) { + // we have probably reached end of file, wrap around from EOF to BOF + closeReplayFile(); + resetReplayFile(); + if ((str = filePointer.readLine()) == null) { + throw MathRuntimeException.createEOFException(LocalizedFormats.URL_CONTAINS_NO_DATA, + valuesFileURL); + } + } + return Double.valueOf(str).doubleValue(); + } + + /** + * Gets a uniformly distributed random value with mean = mu. + * + * @return random uniform value + */ + private double getNextUniform() { + return randomData.nextUniform(0, 2 * mu); + } + + /** + * Gets an exponentially distributed random value with mean = mu. + * + * @return random exponential value + */ + private double getNextExponential() { + return randomData.nextExponential(mu); + } + + /** + * Gets a Gaussian distributed random value with mean = mu + * and standard deviation = sigma. + * + * @return random Gaussian value + */ + private double getNextGaussian() { + return randomData.nextGaussian(mu, sigma); + } + +} |