MATH-1346

Class removed: similar functionality is available from "EmpiricalDistribution",
"RandomUtils.DataGenerator" and classes in package "o.a.c.m.distribution".
This commit is contained in:
Gilles 2016-05-19 18:32:08 +02:00
parent dcde92e7d8
commit e491455737
2 changed files with 0 additions and 687 deletions

View File

@ -1,445 +0,0 @@
/*
* 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.math4.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.math4.exception.MathIllegalArgumentException;
import org.apache.commons.math4.exception.MathIllegalStateException;
import org.apache.commons.math4.exception.NullArgumentException;
import org.apache.commons.math4.exception.ZeroException;
import org.apache.commons.math4.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>
*
*
*/
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 = &mu;. */
public static final int UNIFORM_MODE = 2;
/** Exponential random deviates with mean = &mu;. */
public static final int EXPONENTIAL_MODE = 3;
/** Gaussian random deviates with mean = &mu;, std dev = &sigma;. */
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 RandomDataGenerator randomData;
// Data generation modes ======================================
/** Creates new ValueServer */
public ValueServer() {
randomData = new RandomDataGenerator();
}
/**
* Construct a ValueServer instance using a RandomGenerator as its source
* of random data.
*
* @since 3.1
* @param generator source of random data
*/
public ValueServer(RandomGenerator generator) {
this.randomData = new RandomDataGenerator(generator);
}
/**
* 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
* @throws MathIllegalStateException if mode is not recognized
* @throws MathIllegalArgumentException if the underlying random generator thwrows one
*/
public double getNext() throws IOException, MathIllegalStateException, MathIllegalArgumentException {
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 new MathIllegalStateException(
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
* @throws MathIllegalStateException if mode is not recognized
* @throws MathIllegalArgumentException if the underlying random generator thwrows one
*/
public void fill(double[] values)
throws IOException, MathIllegalStateException, MathIllegalArgumentException {
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
* @throws MathIllegalStateException if mode is not recognized
* @throws MathIllegalArgumentException if the underlying random generator thwrows one
*/
public double[] fill(int length)
throws IOException, MathIllegalStateException, MathIllegalArgumentException {
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
* @throws NullArgumentException if the {@code valuesFileURL} has not been set
* @throws ZeroException if URL contains no data
*/
public void computeDistribution() throws IOException, ZeroException, NullArgumentException {
computeDistribution(EmpiricalDistribution.DEFAULT_BIN_COUNT);
}
/**
* 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 NullArgumentException if the {@code valuesFileURL} has not been set
* @throws IOException if an error occurs reading the input file
* @throws ZeroException if URL contains no data
*/
public void computeDistribution(int binCount) throws NullArgumentException, IOException, ZeroException {
empiricalDistribution = new EmpiricalDistribution(binCount);
empiricalDistribution.load(valuesFileURL);
mu = empiricalDistribution.getSampleStats().getMean();
sigma = empiricalDistribution.getSampleStats().getStandardDeviation();
}
/**
* Returns the data generation mode. See {@link ValueServer the class javadoc}
* for description of the valid values of this property.
*
* @return Value of property mode.
*/
public int getMode() {
return mode;
}
/**
* Sets the data generation mode.
*
* @param mode New value of the data generation mode.
*/
public void setMode(int mode) {
this.mode = mode;
}
/**
* Returns the URL for the file used to build the empirical distribution
* when using {@link #DIGEST_MODE}.
*
* @return Values file URL.
*/
public URL getValuesFileURL() {
return valuesFileURL;
}
/**
* Sets the {@link #getValuesFileURL() values file URL} 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 the {@link #getValuesFileURL() values file URL}.
*
* <p>The values file <i>must</i> be an ASCII text file containing one
* valid numeric entry per line.</p>
*
* @param url URL of the values file.
*/
public void setValuesFileURL(URL url) {
this.valuesFileURL = url;
}
/**
* Returns the {@link EmpiricalDistribution} used when operating in {@value #DIGEST_MODE}.
*
* @return EmpircalDistribution built by {@link #computeDistribution()}
*/
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
* @throws NullPointerException if the {@code valuesFileURL} has not been set.
*/
public void resetReplayFile() throws IOException {
if (filePointer != null) {
try {
filePointer.close();
filePointer = null;
} catch (IOException ex) { //NOPMD
// ignore
}
}
filePointer = new BufferedReader(new InputStreamReader(valuesFileURL.openStream(), "UTF-8"));
}
/**
* Closes {@code valuesFileURL} 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;
}
}
/**
* Returns the mean used when operating in {@link #GAUSSIAN_MODE}, {@link #EXPONENTIAL_MODE}
* or {@link #UNIFORM_MODE}. When operating in {@link #CONSTANT_MODE}, this is the constant
* value always returned. Calling {@link #computeDistribution()} sets this value to the
* overall mean of the values in the {@link #getValuesFileURL() values file}.
*
* @return Mean used in data generation.
*/
public double getMu() {
return mu;
}
/**
* Sets the {@link #getMu() mean} used in data generation. Note that calling this method
* after {@link #computeDistribution()} has been called will have no effect on data
* generated in {@link #DIGEST_MODE}.
*
* @param mu new Mean value.
*/
public void setMu(double mu) {
this.mu = mu;
}
/**
* Returns the standard deviation used when operating in {@link #GAUSSIAN_MODE}.
* Calling {@link #computeDistribution()} sets this value to the overall standard
* deviation of the values in the {@link #getValuesFileURL() values file}. This
* property has no effect when the data generation mode is not
* {@link #GAUSSIAN_MODE}.
*
* @return Standard deviation used when operating in {@link #GAUSSIAN_MODE}.
*/
public double getSigma() {
return sigma;
}
/**
* Sets the {@link #getSigma() standard deviation} used in {@link #GAUSSIAN_MODE}.
*
* @param sigma New standard deviation.
*/
public void setSigma(double sigma) {
this.sigma = sigma;
}
/**
* Reseeds the random data generator.
*
* @param seed Value with which to reseed the {@link RandomDataGenerator}
* used to generate random data.
*/
public void reSeed(long seed) {
randomData.reSeed(seed);
}
//------------- 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
* @throws MathIllegalStateException if digest has not been initialized
*/
private double getNextDigest() throws MathIllegalStateException {
if ((empiricalDistribution == null) ||
(empiricalDistribution.getBinStats().size() == 0)) {
throw new MathIllegalStateException(LocalizedFormats.DIGEST_NOT_INITIALIZED);
}
return empiricalDistribution.createSampler(randomData.getRandomProvider()).sample();
}
/**
* 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 MathIllegalStateException if URL contains no data
* @throws NumberFormatException if an invalid numeric string is
* encountered in the file
*/
private double getNextReplay() throws IOException, MathIllegalStateException {
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 new MathIllegalStateException(LocalizedFormats.URL_CONTAINS_NO_DATA,
valuesFileURL);
}
}
return Double.parseDouble(str);
}
/**
* Gets a uniformly distributed random value with mean = mu.
*
* @return random uniform value
* @throws MathIllegalArgumentException if the underlying random generator thwrows one
*/
private double getNextUniform() throws MathIllegalArgumentException {
return randomData.nextUniform(0, 2 * mu);
}
/**
* Gets an exponentially distributed random value with mean = mu.
*
* @return random exponential value
* @throws MathIllegalArgumentException if the underlying random generator thwrows one
*/
private double getNextExponential() throws MathIllegalArgumentException {
return randomData.nextExponential(mu);
}
/**
* Gets a Gaussian distributed random value with mean = mu
* and standard deviation = sigma.
*
* @return random Gaussian value
* @throws MathIllegalArgumentException if the underlying random generator thwrows one
*/
private double getNextGaussian() throws MathIllegalArgumentException {
return randomData.nextGaussian(mu, sigma);
}
}

View File

@ -1,242 +0,0 @@
/*
* 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.math4.random;
import java.net.URL;
import java.util.Arrays;
import org.apache.commons.math4.RetryRunner;
import org.apache.commons.math4.exception.MathIllegalStateException;
import org.apache.commons.math4.exception.ZeroException;
import org.apache.commons.math4.random.ValueServer;
import org.apache.commons.math4.random.Well19937c;
import org.apache.commons.math4.stat.descriptive.SummaryStatistics;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Test cases for the ValueServer class.
*
*/
@RunWith(RetryRunner.class)
public final class ValueServerTest {
private final ValueServer vs = new ValueServer(new Well19937c(100));
@Before
public void setUp() {
vs.setMode(ValueServer.DIGEST_MODE);
URL url = getClass().getResource("testData.txt");
vs.setValuesFileURL(url);
}
/**
* Generate 1000 random values and make sure they look OK.<br>
* Note that there is a non-zero (but very small) probability that
* these tests will fail even if the code is working as designed.
*/
@Test
public void testNextDigest() throws Exception {
double next = 0.0;
double tolerance = 0.1;
vs.computeDistribution();
Assert.assertTrue("empirical distribution property",
vs.getEmpiricalDistribution() != null);
SummaryStatistics stats = new SummaryStatistics();
for (int i = 1; i < 1000; i++) {
next = vs.getNext();
stats.addValue(next);
}
Assert.assertEquals("mean", 5.069831575018909, stats.getMean(), tolerance);
Assert.assertEquals("std dev", 1.0173699343977738, stats.getStandardDeviation(),
tolerance);
vs.computeDistribution(500);
stats = new SummaryStatistics();
for (int i = 1; i < 1000; i++) {
next = vs.getNext();
stats.addValue(next);
}
Assert.assertEquals("mean", 5.069831575018909, stats.getMean(), tolerance);
Assert.assertEquals("std dev", 1.0173699343977738, stats.getStandardDeviation(),
tolerance);
}
/**
* Verify that when provided with fixed seeds, stochastic modes
* generate fixed sequences. Verifies the fix for MATH-654.
*/
@Test
public void testFixedSeed() throws Exception {
ValueServer valueServer = new ValueServer();
URL url = getClass().getResource("testData.txt");
valueServer.setValuesFileURL(url);
valueServer.computeDistribution();
checkFixedSeed(valueServer, ValueServer.DIGEST_MODE);
checkFixedSeed(valueServer, ValueServer.EXPONENTIAL_MODE);
checkFixedSeed(valueServer, ValueServer.GAUSSIAN_MODE);
checkFixedSeed(valueServer, ValueServer.UNIFORM_MODE);
}
/**
* Do the check for {@link #testFixedSeed()}
* @param mode ValueServer mode
*/
private void checkFixedSeed(ValueServer valueServer, int mode) throws Exception {
valueServer.reSeed(1000);
valueServer.setMode(mode);
double[][] values = new double[2][100];
for (int i = 0; i < 100; i++) {
values[0][i] = valueServer.getNext();
}
valueServer.reSeed(1000);
for (int i = 0; i < 100; i++) {
values[1][i] = valueServer.getNext();
}
Assert.assertTrue(Arrays.equals(values[0], values[1]));
}
/**
* Make sure exception thrown if digest getNext is attempted
* before loading empiricalDistribution.
*/
@Test
public void testNextDigestFail() throws Exception {
try {
vs.getNext();
Assert.fail("Expecting MathIllegalStateException");
} catch (MathIllegalStateException ex) {}
}
@Test
public void testEmptyReplayFile() throws Exception {
try {
URL url = getClass().getResource("emptyFile.txt");
vs.setMode(ValueServer.REPLAY_MODE);
vs.setValuesFileURL(url);
vs.getNext();
Assert.fail("an exception should have been thrown");
} catch (MathIllegalStateException mise) {
// expected behavior
}
}
@Test
public void testEmptyDigestFile() throws Exception {
try {
URL url = getClass().getResource("emptyFile.txt");
vs.setMode(ValueServer.DIGEST_MODE);
vs.setValuesFileURL(url);
vs.computeDistribution();
Assert.fail("an exception should have been thrown");
} catch (ZeroException ze) {
// expected behavior
}
}
/**
* Test ValueServer REPLAY_MODE using values in testData file.<br>
* Check that the values 1,2,1001,1002 match data file values 1 and 2.
* the sample data file.
*/
@Test
public void testReplay() throws Exception {
double firstDataValue = 4.038625496201205;
double secondDataValue = 3.6485326248346936;
double tolerance = 10E-15;
double compareValue = 0.0d;
vs.setMode(ValueServer.REPLAY_MODE);
vs.resetReplayFile();
compareValue = vs.getNext();
Assert.assertEquals(compareValue,firstDataValue,tolerance);
compareValue = vs.getNext();
Assert.assertEquals(compareValue,secondDataValue,tolerance);
for (int i = 3; i < 1001; i++) {
compareValue = vs.getNext();
}
compareValue = vs.getNext();
Assert.assertEquals(compareValue,firstDataValue,tolerance);
compareValue = vs.getNext();
Assert.assertEquals(compareValue,secondDataValue,tolerance);
vs.closeReplayFile();
// make sure no NPE
vs.closeReplayFile();
}
/**
* Test other ValueServer modes
*/
@Test
public void testModes() throws Exception {
vs.setMode(ValueServer.CONSTANT_MODE);
vs.setMu(0);
Assert.assertEquals("constant mode test",vs.getMu(),vs.getNext(),Double.MIN_VALUE);
vs.setMode(ValueServer.UNIFORM_MODE);
vs.setMu(2);
double val = vs.getNext();
Assert.assertTrue(val > 0 && val < 4);
vs.setSigma(1);
vs.setMode(ValueServer.GAUSSIAN_MODE);
val = vs.getNext();
Assert.assertTrue("gaussian value close enough to mean",
val < vs.getMu() + 100*vs.getSigma());
vs.setMode(ValueServer.EXPONENTIAL_MODE);
val = vs.getNext();
Assert.assertTrue(val > 0);
try {
vs.setMode(1000);
vs.getNext();
Assert.fail("bad mode, expecting MathIllegalStateException");
} catch (MathIllegalStateException ex) {
// ignored
}
}
/**
* Test fill
*/
@Test
public void testFill() throws Exception {
vs.setMode(ValueServer.CONSTANT_MODE);
vs.setMu(2);
double[] val = new double[5];
vs.fill(val);
for (int i = 0; i < 5; i++) {
Assert.assertEquals("fill test in place",2,val[i],Double.MIN_VALUE);
}
double v2[] = vs.fill(3);
for (int i = 0; i < 3; i++) {
Assert.assertEquals("fill test in place",2,v2[i],Double.MIN_VALUE);
}
}
/**
* Test getters to make Clover happy
*/
@Test
public void testProperties() throws Exception {
vs.setMode(ValueServer.CONSTANT_MODE);
Assert.assertEquals("mode test",ValueServer.CONSTANT_MODE,vs.getMode());
vs.setValuesFileURL("http://www.apache.org");
URL url = vs.getValuesFileURL();
Assert.assertEquals("valuesFileURL test","http://www.apache.org",url.toString());
}
}