1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.math.random;
19 import java.io.BufferedReader;
20 import java.io.EOFException;
21 import java.io.InputStreamReader;
22 import java.io.IOException;
23 import java.net.URL;
24 import java.net.MalformedURLException;
25
26 /**
27 * Generates values for use in simulation applications.
28 * <p>
29 * How values are generated is determined by the <code>mode</code>
30 * property.</p>
31 * <p>
32 * Supported <code>mode</code> values are: <ul>
33 * <li> DIGEST_MODE -- uses an empirical distribution </li>
34 * <li> REPLAY_MODE -- replays data from <code>valuesFileURL</code></li>
35 * <li> UNIFORM_MODE -- generates uniformly distributed random values with
36 * mean = <code>mu</code> </li>
37 * <li> EXPONENTIAL_MODE -- generates exponentially distributed random values
38 * with mean = <code>mu</code></li>
39 * <li> GAUSSIAN_MODE -- generates Gaussian distributed random values with
40 * mean = <code>mu</code> and
41 * standard deviation = <code>sigma</code></li>
42 * <li> CONSTANT_MODE -- returns <code>mu</code> every time.</li></ul></p>
43 *
44 * @version $Revision: 617850 $ $Date: 2008-02-02 11:01:29 -0700 (Sat, 02 Feb 2008) $
45 *
46 */
47 public class ValueServer {
48 /** mode determines how values are generated */
49 private int mode = 5;
50
51 /** URI to raw data values */
52 private URL valuesFileURL = null;
53
54 /** Mean for use with non-data-driven modes */
55 private double mu = 0.0;
56
57 /** Standard deviation for use with GAUSSIAN_MODE */
58 private double sigma = 0.0;
59
60 /** Empirical probability distribution for use with DIGEST_MODE */
61 private EmpiricalDistribution empiricalDistribution = null;
62
63 /** file pointer for REPLAY_MODE */
64 private BufferedReader filePointer = null;
65
66 /** RandomDataImpl to use for random data generation */
67 private RandomData randomData = new RandomDataImpl();
68
69 // Data generation modes ======================================
70
71 /** Use empirical distribution */
72 public static final int DIGEST_MODE = 0;
73
74 /** Replay data from valuesFilePath */
75 public static final int REPLAY_MODE = 1;
76
77 /** Uniform random deviates with mean = mu */
78 public static final int UNIFORM_MODE = 2;
79
80 /** Exponential random deviates with mean = mu */
81 public static final int EXPONENTIAL_MODE = 3;
82
83 /** Gaussian random deviates with mean = mu, std dev = sigma */
84 public static final int GAUSSIAN_MODE = 4;
85
86 /** Always return mu */
87 public static final int CONSTANT_MODE = 5;
88
89 /** Creates new ValueServer */
90 public ValueServer() {
91 }
92
93 /**
94 * Returns the next generated value, generated according
95 * to the mode value (see MODE constants).
96 *
97 * @return generated value
98 * @throws IOException in REPLAY_MODE if a file I/O error occurs
99 */
100 public double getNext() throws IOException {
101 switch (mode) {
102 case DIGEST_MODE: return getNextDigest();
103 case REPLAY_MODE: return getNextReplay();
104 case UNIFORM_MODE: return getNextUniform();
105 case EXPONENTIAL_MODE: return getNextExponential();
106 case GAUSSIAN_MODE: return getNextGaussian();
107 case CONSTANT_MODE: return mu;
108 default: throw new IllegalStateException
109 ("Bad mode: " + mode);
110 }
111 }
112
113 /**
114 * Fills the input array with values generated using getNext() repeatedly.
115 *
116 * @param values array to be filled
117 * @throws IOException in REPLAY_MODE if a file I/O error occurs
118 */
119 public void fill(double[] values) throws IOException {
120 for (int i = 0; i < values.length; i++) {
121 values[i] = getNext();
122 }
123 }
124
125 /**
126 * Returns an array of length <code>length</code> with values generated
127 * using getNext() repeatedly.
128 *
129 * @param length length of output array
130 * @return array of generated values
131 * @throws IOException in REPLAY_MODE if a file I/O error occurs
132 */
133 public double[] fill(int length) throws IOException {
134 double[] out = new double[length];
135 for (int i = 0; i < length; i++) {
136 out[i] = getNext();
137 }
138 return out;
139 }
140
141 /**
142 * Computes the empirical distribution using values from the file
143 * in <code>valuesFileURL</code>, using the default number of bins.
144 * <p>
145 * <code>valuesFileURL</code> must exist and be
146 * readable by *this at runtime.</p>
147 * <p>
148 * This method must be called before using <code>getNext()</code>
149 * with <code>mode = DIGEST_MODE</code></p>
150 *
151 * @throws IOException if an I/O error occurs reading the input file
152 */
153 public void computeDistribution() throws IOException {
154 empiricalDistribution = new EmpiricalDistributionImpl();
155 empiricalDistribution.load(valuesFileURL);
156 }
157
158 /**
159 * Computes the empirical distribution using values from the file
160 * in <code>valuesFileURL</code> and <code>binCount</code> bins.
161 * <p>
162 * <code>valuesFileURL</code> must exist and be readable by this process
163 * at runtime.</p>
164 * <p>
165 * This method must be called before using <code>getNext()</code>
166 * with <code>mode = DIGEST_MODE</code></p>
167 *
168 * @param binCount the number of bins used in computing the empirical
169 * distribution
170 * @throws IOException if an error occurs reading the input file
171 */
172 public void computeDistribution(int binCount)
173 throws IOException {
174 empiricalDistribution = new EmpiricalDistributionImpl(binCount);
175 empiricalDistribution.load(valuesFileURL);
176 mu = empiricalDistribution.getSampleStats().getMean();
177 sigma = empiricalDistribution.getSampleStats().getStandardDeviation();
178 }
179
180 /** Getter for property mode.
181 * @return Value of property mode.
182 */
183 public int getMode() {
184 return mode;
185 }
186
187 /** Setter for property mode.
188 * @param mode New value of property mode.
189 */
190 public void setMode(int mode) {
191 this.mode = mode;
192 }
193
194 /**
195 * Getter for <code>valuesFileURL<code>
196 * @return Value of property valuesFileURL.
197 */
198 public URL getValuesFileURL() {
199 return valuesFileURL;
200 }
201
202 /**
203 * Sets the <code>valuesFileURL</code> using a string URL representation
204 * @param url String representation for new valuesFileURL.
205 * @throws MalformedURLException if url is not well formed
206 */
207 public void setValuesFileURL(String url) throws MalformedURLException {
208 this.valuesFileURL = new URL(url);
209 }
210
211 /**
212 * Sets the <code>valuesFileURL</code>
213 * @param url New value of property valuesFileURL.
214 */
215 public void setValuesFileURL(URL url) {
216 this.valuesFileURL = url;
217 }
218
219 /** Getter for property empiricalDistribution.
220 * @return Value of property empiricalDistribution.
221 */
222 public EmpiricalDistribution getEmpiricalDistribution() {
223 return empiricalDistribution;
224 }
225
226 /**
227 * Resets REPLAY_MODE file pointer to the beginning of the <code>valuesFileURL</code>.
228 *
229 * @throws IOException if an error occurs opening the file
230 */
231 public void resetReplayFile() throws IOException {
232 if (filePointer != null) {
233 try {
234 filePointer.close();
235 filePointer = null;
236 } catch (IOException ex) {
237 // ignore
238 }
239 }
240 filePointer = new BufferedReader(new InputStreamReader(valuesFileURL.openStream()));
241 }
242
243 /**
244 * Closes <code>valuesFileURL</code> after use in REPLAY_MODE.
245 *
246 * @throws IOException if an error occurs closing the file
247 */
248 public void closeReplayFile() throws IOException {
249 if (filePointer != null) {
250 filePointer.close();
251 filePointer = null;
252 }
253 }
254
255 /** Getter for property mu.
256 * @return Value of property mu.
257 */
258 public double getMu() {
259 return mu;
260 }
261
262 /** Setter for property mu.
263 * @param mu New value of property mu.
264 */
265 public void setMu(double mu) {
266 this.mu = mu;
267 }
268
269 /** Getter for property sigma.
270 * @return Value of property sigma.
271 */
272 public double getSigma() {
273 return sigma;
274 }
275
276 /** Setter for property sigma.
277 * @param sigma New value of property sigma.
278 */
279 public void setSigma(double sigma) {
280 this.sigma = sigma;
281 }
282
283 //------------- private methods ---------------------------------
284
285 /**
286 * Gets a random value in DIGEST_MODE.
287 * <p>
288 * <strong>Preconditions</strong>: <ul>
289 * <li>Before this method is called, <code>computeDistribution()</code>
290 * must have completed successfully; otherwise an
291 * <code>IllegalStateException</code> will be thrown</li></ul></p>
292 *
293 * @return next random value from the empirical distribution digest
294 */
295 private double getNextDigest() {
296 if ((empiricalDistribution == null) ||
297 (empiricalDistribution.getBinStats().size() == 0)) {
298 throw new IllegalStateException("Digest not initialized");
299 }
300 return empiricalDistribution.getNextValue();
301 }
302
303 /**
304 * Gets next sequential value from the <code>valuesFileURL</code>.
305 * <p>
306 * Throws an IOException if the read fails.</p>
307 * <p>
308 * This method will open the <code>valuesFileURL</code> if there is no
309 * replay file open.</p>
310 * <p>
311 * The <code>valuesFileURL</code> will be closed and reopened to wrap around
312 * from EOF to BOF if EOF is encountered. EOFException (which is a kind of
313 * IOException) may still be thrown if the <code>valuesFileURL</code> is
314 * empty.</p>
315 *
316 * @return next value from the replay file
317 * @throws IOException if there is a problem reading from the file
318 * @throws NumberFormatException if an invalid numeric string is
319 * encountered in the file
320 */
321 private double getNextReplay() throws IOException {
322 String str = null;
323 if (filePointer == null) {
324 resetReplayFile();
325 }
326 if ((str = filePointer.readLine()) == null) {
327 // we have probably reached end of file, wrap around from EOF to BOF
328 closeReplayFile();
329 resetReplayFile();
330 if ((str = filePointer.readLine()) == null) {
331 throw new EOFException("URL " + valuesFileURL + " contains no data");
332 }
333 }
334 return Double.valueOf(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 }