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