001: /*
002: * Copyright 2003-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.commons.math.random;
018:
019: import java.io.BufferedReader;
020: import java.io.InputStreamReader;
021: import java.io.IOException;
022: import java.net.URL;
023: import java.net.MalformedURLException;
024:
025: /**
026: * Generates values for use in simulation applications.
027: * <p>
028: * How values are generated is determined by the <code>mode</code>
029: * property.
030: * <p>
031: * Supported <code>mode</code> values are: <ul>
032: * <li> DIGEST_MODE -- uses an empirical distribution </li>
033: * <li> REPLAY_MODE -- replays data from <code>valuesFileURL</code></li>
034: * <li> UNIFORM_MODE -- generates uniformly distributed random values with
035: * mean = <code>mu</code> </li>
036: * <li> EXPONENTIAL_MODE -- generates exponentially distributed random values
037: * with mean = <code>mu</code></li>
038: * <li> GAUSSIAN_MODE -- generates Gaussian distributed random values with
039: * mean = <code>mu</code> and
040: * standard deviation = <code>sigma</code></li>
041: * <li> CONSTANT_MODE -- returns <code>mu</code> every time.</li></ul>
042: *
043: * @version $Revision: 171283 $ $Date: 2005-05-21 22:25:44 -0700 (Sat, 21 May 2005) $
044: *
045: */
046: public class ValueServer {
047: /** mode determines how values are generated */
048: private int mode = 5;
049:
050: /** URI to raw data values */
051: private URL valuesFileURL = null;
052:
053: /** Mean for use with non-data-driven modes */
054: private double mu = 0.0;
055:
056: /** Standard deviation for use with GAUSSIAN_MODE */
057: private double sigma = 0.0;
058:
059: /** Empirical probability distribution for use with DIGEST_MODE */
060: private EmpiricalDistribution empiricalDistribution = null;
061:
062: /** file pointer for REPLAY_MODE */
063: private BufferedReader filePointer = null;
064:
065: /** RandomDataImpl to use for random data generation */
066: private RandomData randomData = new RandomDataImpl();
067:
068: // Data generation modes ======================================
069:
070: /** Use empirical distribution */
071: public static final int DIGEST_MODE = 0;
072:
073: /** Replay data from valuesFilePath */
074: public static final int REPLAY_MODE = 1;
075:
076: /** Uniform random deviates with mean = mu */
077: public static final int UNIFORM_MODE = 2;
078:
079: /** Exponential random deviates with mean = mu */
080: public static final int EXPONENTIAL_MODE = 3;
081:
082: /** Gaussian random deviates with mean = mu, std dev = sigma */
083: public static final int GAUSSIAN_MODE = 4;
084:
085: /** Always return mu */
086: public static final int CONSTANT_MODE = 5;
087:
088: /** Creates new ValueServer */
089: public ValueServer() {
090: }
091:
092: /**
093: * Returns the next generated value, generated according
094: * to the mode value (see MODE constants).
095: *
096: * @return generated value
097: * @throws IOException in REPLAY_MODE if a file I/O error occurs
098: */
099: public double getNext() throws IOException {
100: switch (mode) {
101: case DIGEST_MODE:
102: return getNextDigest();
103: case REPLAY_MODE:
104: return getNextReplay();
105: case UNIFORM_MODE:
106: return getNextUniform();
107: case EXPONENTIAL_MODE:
108: return getNextExponential();
109: case GAUSSIAN_MODE:
110: return getNextGaussian();
111: case CONSTANT_MODE:
112: return mu;
113: default:
114: throw new IllegalStateException("Bad mode: " + mode);
115: }
116: }
117:
118: /**
119: * Fills the input array with values generated using getNext() repeatedly.
120: *
121: * @param values array to be filled
122: * @throws IOException in REPLAY_MODE if a file I/O error occurs
123: */
124: public void fill(double[] values) throws IOException {
125: for (int i = 0; i < values.length; i++) {
126: values[i] = getNext();
127: }
128: }
129:
130: /**
131: * Returns an array of length <code>length</code> with values generated
132: * using getNext() repeatedly.
133: *
134: * @param length length of output array
135: * @return array of generated values
136: * @throws IOException in REPLAY_MODE if a file I/O error occurs
137: */
138: public double[] fill(int length) throws IOException {
139: double[] out = new double[length];
140: for (int i = 0; i < length; i++) {
141: out[i] = getNext();
142: }
143: return out;
144: }
145:
146: /**
147: * Computes the empirical distribution using values from the file
148: * in <code>valuesFileURL</code>, using the default number of bins.
149: * <p>
150: * <code>valuesFileURL</code> must exist and be
151: * readable by *this at runtime.
152: * <p>
153: * This method must be called before using <code>getNext()</code>
154: * with <code>mode = DISGEST_MODE</code>
155: *
156: * @throws IOException if an I/O error occurs reading the input file
157: */
158: public void computeDistribution() throws IOException {
159: empiricalDistribution = new EmpiricalDistributionImpl();
160: empiricalDistribution.load(valuesFileURL);
161: }
162:
163: /**
164: * Computes the empirical distribution using values from the file
165: * in <code>valuesFileURL</code> and <code>binCount</code> bins.
166: * <p>
167: * <code>valuesFileURL</code> must exist and be
168: * readable by *this at runtime.
169: * <p>
170: * This method must be called before using <code>getNext()</code>
171: * with <code>mode = DISGEST_MODE</code>
172: *
173: * @param binCount the number of bins used in computing the empirical
174: * distribution
175: * @throws IOException if an error occurs reading the input file
176: */
177: public void computeDistribution(int binCount) throws IOException {
178: empiricalDistribution = new EmpiricalDistributionImpl(binCount);
179: empiricalDistribution.load(valuesFileURL);
180: mu = empiricalDistribution.getSampleStats().getMean();
181: sigma = empiricalDistribution.getSampleStats()
182: .getStandardDeviation();
183: }
184:
185: /** Getter for property mode.
186: * @return Value of property mode.
187: */
188: public int getMode() {
189: return mode;
190: }
191:
192: /** Setter for property mode.
193: * @param mode New value of property mode.
194: */
195: public void setMode(int mode) {
196: this .mode = mode;
197: }
198:
199: /**
200: * Getter for <code>valuesFileURL<code>
201: * @return Value of property valuesFileURL.
202: */
203: public URL getValuesFileURL() {
204: return valuesFileURL;
205: }
206:
207: /**
208: * Sets the <code>valuesFileURL</code> using a string URL representation
209: * @param url String representation for new valuesFileURL.
210: * @throws MalformedURLException if url is not well formed
211: */
212: public void setValuesFileURL(String url)
213: throws MalformedURLException {
214: this .valuesFileURL = new URL(url);
215: }
216:
217: /**
218: * Sets the <code>valuesFileURL</code>
219: * @param url New value of property valuesFileURL.
220: */
221: public void setValuesFileURL(URL url) {
222: this .valuesFileURL = url;
223: }
224:
225: /** Getter for property empiricalDistribution.
226: * @return Value of property empiricalDistribution.
227: */
228: public EmpiricalDistribution getEmpiricalDistribution() {
229: return empiricalDistribution;
230: }
231:
232: /**
233: * Resets REPLAY_MODE file pointer to the beginning of the <code>valuesFileURL</code>.
234: *
235: * @throws IOException if an error occurs opening the file
236: */
237: public void resetReplayFile() throws IOException {
238: if (filePointer != null) {
239: try {
240: filePointer.close();
241: filePointer = null;
242: } catch (IOException ex) {
243: // ignore
244: }
245: }
246: filePointer = new BufferedReader(new InputStreamReader(
247: valuesFileURL.openStream()));
248: }
249:
250: /**
251: * Closes <code>valuesFileURL</code> after use in REPLAY_MODE.
252: *
253: * @throws IOException if an error occurs closing the file
254: */
255: public void closeReplayFile() throws IOException {
256: if (filePointer != null) {
257: filePointer.close();
258: filePointer = null;
259: }
260: }
261:
262: /** Getter for property mu.
263: * @return Value of property mu.
264: */
265: public double getMu() {
266: return mu;
267: }
268:
269: /** Setter for property mu.
270: * @param mu New value of property mu.
271: */
272: public void setMu(double mu) {
273: this .mu = mu;
274: }
275:
276: /** Getter for property sigma.
277: * @return Value of property sigma.
278: */
279: public double getSigma() {
280: return sigma;
281: }
282:
283: /** Setter for property sigma.
284: * @param sigma New value of property sigma.
285: */
286: public void setSigma(double sigma) {
287: this .sigma = sigma;
288: }
289:
290: //------------- private methods ---------------------------------
291:
292: /**
293: * Gets a random value in DIGEST_MODE.
294: * <p>
295: * <strong>Preconditions</strong>: <ul>
296: * <li>Before this method is called, <code>computeDistribution()</code>
297: * must have completed successfully; otherwise an
298: * <code>IllegalStateException</code> will be thrown</li></ul>
299: *
300: * @return next random value from the empirical distribution digest
301: */
302: private double getNextDigest() {
303: if ((empiricalDistribution == null)
304: || (empiricalDistribution.getBinStats().size() == 0)) {
305: throw new IllegalStateException("Digest not initialized");
306: }
307: return empiricalDistribution.getNextValue();
308: }
309:
310: /**
311: * Gets next sequential value from the <code>valuesFileURL</code>.
312: * <p>
313: * Throws an IOException if the read fails.
314: * <p>
315: * This method will open the <code>valuesFileURL</code> if there is no
316: * replay file open.
317: * <p>
318: * The <code>valuesFileURL</code> will be closed and reopened to wrap around
319: * from EOF to BOF if EOF is encountered.
320: *
321: * @return next value from the replay file
322: * @throws IOException if there is a problem reading from the file
323: */
324: private double getNextReplay() throws IOException {
325: String str = null;
326: if (filePointer == null) {
327: resetReplayFile();
328: }
329: if ((str = filePointer.readLine()) == null) {
330: closeReplayFile();
331: resetReplayFile();
332: str = filePointer.readLine();
333: }
334: return new Double(str).doubleValue();
335: }
336:
337: /**
338: * Gets a uniformly distributed random value with mean = mu.
339: *
340: * @return random uniform value
341: */
342: private double getNextUniform() {
343: return randomData.nextUniform(0, 2 * mu);
344: }
345:
346: /**
347: * Gets an exponentially distributed random value with mean = mu.
348: *
349: * @return random exponential value
350: */
351: private double getNextExponential() {
352: return randomData.nextExponential(mu);
353: }
354:
355: /**
356: * Gets a Gaussian distributed random value with mean = mu
357: * and standard deviation = sigma.
358: *
359: * @return random Gaussian value
360: */
361: private double getNextGaussian() {
362: return randomData.nextGaussian(mu, sigma);
363: }
364:
365: /**
366: * Construct a ValueServer instance using a RandomData as its source
367: * of random data.
368: *
369: * @param randomData the RandomData instance used to source random data
370: * @since 1.1
371: */
372: public ValueServer(RandomData randomData) {
373: super();
374: this.randomData = randomData;
375: }
376: }
|