blob: 9146e69cd6c0883ee557ddd5409562ffb3a8dc1f [file] [log] [blame]
Raymonddee08492015-04-02 10:43:13 -07001/*
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
18package org.apache.commons.math.random;
19import java.io.BufferedReader;
20import java.io.IOException;
21import java.io.InputStreamReader;
22import java.net.MalformedURLException;
23import java.net.URL;
24
25import org.apache.commons.math.MathRuntimeException;
26import org.apache.commons.math.exception.util.LocalizedFormats;
27
28/**
29 * Generates values for use in simulation applications.
30 * <p>
31 * How values are generated is determined by the <code>mode</code>
32 * property.</p>
33 * <p>
34 * Supported <code>mode</code> values are: <ul>
35 * <li> DIGEST_MODE -- uses an empirical distribution </li>
36 * <li> REPLAY_MODE -- replays data from <code>valuesFileURL</code></li>
37 * <li> UNIFORM_MODE -- generates uniformly distributed random values with
38 * mean = <code>mu</code> </li>
39 * <li> EXPONENTIAL_MODE -- generates exponentially distributed random values
40 * with mean = <code>mu</code></li>
41 * <li> GAUSSIAN_MODE -- generates Gaussian distributed random values with
42 * mean = <code>mu</code> and
43 * standard deviation = <code>sigma</code></li>
44 * <li> CONSTANT_MODE -- returns <code>mu</code> every time.</li></ul></p>
45 *
46 * @version $Revision: 1003886 $ $Date: 2010-10-02 23:04:44 +0200 (sam. 02 oct. 2010) $
47 *
48 */
49public class ValueServer {
50
51 /** Use empirical distribution. */
52 public static final int DIGEST_MODE = 0;
53
54 /** Replay data from valuesFilePath. */
55 public static final int REPLAY_MODE = 1;
56
57 /** Uniform random deviates with mean = &mu;. */
58 public static final int UNIFORM_MODE = 2;
59
60 /** Exponential random deviates with mean = &mu;. */
61 public static final int EXPONENTIAL_MODE = 3;
62
63 /** Gaussian random deviates with mean = &mu;, std dev = &sigma;. */
64 public static final int GAUSSIAN_MODE = 4;
65
66 /** Always return mu */
67 public static final int CONSTANT_MODE = 5;
68
69 /** mode determines how values are generated. */
70 private int mode = 5;
71
72 /** URI to raw data values. */
73 private URL valuesFileURL = null;
74
75 /** Mean for use with non-data-driven modes. */
76 private double mu = 0.0;
77
78 /** Standard deviation for use with GAUSSIAN_MODE. */
79 private double sigma = 0.0;
80
81 /** Empirical probability distribution for use with DIGEST_MODE. */
82 private EmpiricalDistribution empiricalDistribution = null;
83
84 /** File pointer for REPLAY_MODE. */
85 private BufferedReader filePointer = null;
86
87 /** RandomDataImpl to use for random data generation. */
88 private final RandomData randomData;
89
90 // Data generation modes ======================================
91
92 /** Creates new ValueServer */
93 public ValueServer() {
94 randomData = new RandomDataImpl();
95 }
96
97 /**
98 * Construct a ValueServer instance using a RandomData as its source
99 * of random data.
100 *
101 * @param randomData the RandomData instance used to source random data
102 * @since 1.1
103 */
104 public ValueServer(RandomData randomData) {
105 this.randomData = randomData;
106 }
107
108 /**
109 * Returns the next generated value, generated according
110 * to the mode value (see MODE constants).
111 *
112 * @return generated value
113 * @throws IOException in REPLAY_MODE if a file I/O error occurs
114 */
115 public double getNext() throws IOException {
116 switch (mode) {
117 case DIGEST_MODE: return getNextDigest();
118 case REPLAY_MODE: return getNextReplay();
119 case UNIFORM_MODE: return getNextUniform();
120 case EXPONENTIAL_MODE: return getNextExponential();
121 case GAUSSIAN_MODE: return getNextGaussian();
122 case CONSTANT_MODE: return mu;
123 default: throw MathRuntimeException.createIllegalStateException(
124 LocalizedFormats.UNKNOWN_MODE,
125 mode,
126 "DIGEST_MODE", DIGEST_MODE, "REPLAY_MODE", REPLAY_MODE,
127 "UNIFORM_MODE", UNIFORM_MODE, "EXPONENTIAL_MODE", EXPONENTIAL_MODE,
128 "GAUSSIAN_MODE", GAUSSIAN_MODE, "CONSTANT_MODE", CONSTANT_MODE);
129 }
130 }
131
132 /**
133 * Fills the input array with values generated using getNext() repeatedly.
134 *
135 * @param values array to be filled
136 * @throws IOException in REPLAY_MODE if a file I/O error occurs
137 */
138 public void fill(double[] values) throws IOException {
139 for (int i = 0; i < values.length; i++) {
140 values[i] = getNext();
141 }
142 }
143
144 /**
145 * Returns an array of length <code>length</code> with values generated
146 * using getNext() repeatedly.
147 *
148 * @param length length of output array
149 * @return array of generated values
150 * @throws IOException in REPLAY_MODE if a file I/O error occurs
151 */
152 public double[] fill(int length) throws IOException {
153 double[] out = new double[length];
154 for (int i = 0; i < length; i++) {
155 out[i] = getNext();
156 }
157 return out;
158 }
159
160 /**
161 * Computes the empirical distribution using values from the file
162 * in <code>valuesFileURL</code>, using the default number of bins.
163 * <p>
164 * <code>valuesFileURL</code> must exist and be
165 * readable by *this at runtime.</p>
166 * <p>
167 * This method must be called before using <code>getNext()</code>
168 * with <code>mode = DIGEST_MODE</code></p>
169 *
170 * @throws IOException if an I/O error occurs reading the input file
171 */
172 public void computeDistribution() throws IOException {
173 empiricalDistribution = new EmpiricalDistributionImpl();
174 empiricalDistribution.load(valuesFileURL);
175 }
176
177 /**
178 * Computes the empirical distribution using values from the file
179 * in <code>valuesFileURL</code> and <code>binCount</code> bins.
180 * <p>
181 * <code>valuesFileURL</code> must exist and be readable by this process
182 * at runtime.</p>
183 * <p>
184 * This method must be called before using <code>getNext()</code>
185 * with <code>mode = DIGEST_MODE</code></p>
186 *
187 * @param binCount the number of bins used in computing the empirical
188 * distribution
189 * @throws IOException if an error occurs reading the input file
190 */
191 public void computeDistribution(int binCount)
192 throws IOException {
193 empiricalDistribution = new EmpiricalDistributionImpl(binCount);
194 empiricalDistribution.load(valuesFileURL);
195 mu = empiricalDistribution.getSampleStats().getMean();
196 sigma = empiricalDistribution.getSampleStats().getStandardDeviation();
197 }
198
199 /** Getter for property mode.
200 * @return Value of property mode.
201 */
202 public int getMode() {
203 return mode;
204 }
205
206 /** Setter for property mode.
207 * @param mode New value of property mode.
208 */
209 public void setMode(int mode) {
210 this.mode = mode;
211 }
212
213 /**
214 * Getter for <code>valuesFileURL<code>
215 * @return Value of property valuesFileURL.
216 */
217 public URL getValuesFileURL() {
218 return valuesFileURL;
219 }
220
221 /**
222 * Sets the <code>valuesFileURL</code> using a string URL representation
223 * @param url String representation for new valuesFileURL.
224 * @throws MalformedURLException if url is not well formed
225 */
226 public void setValuesFileURL(String url) throws MalformedURLException {
227 this.valuesFileURL = new URL(url);
228 }
229
230 /**
231 * Sets the <code>valuesFileURL</code>
232 * @param url New value of property valuesFileURL.
233 */
234 public void setValuesFileURL(URL url) {
235 this.valuesFileURL = url;
236 }
237
238 /** Getter for property empiricalDistribution.
239 * @return Value of property empiricalDistribution.
240 */
241 public EmpiricalDistribution getEmpiricalDistribution() {
242 return empiricalDistribution;
243 }
244
245 /**
246 * Resets REPLAY_MODE file pointer to the beginning of the <code>valuesFileURL</code>.
247 *
248 * @throws IOException if an error occurs opening the file
249 */
250 public void resetReplayFile() throws IOException {
251 if (filePointer != null) {
252 try {
253 filePointer.close();
254 filePointer = null;
255 } catch (IOException ex) {
256 // ignore
257 }
258 }
259 filePointer = new BufferedReader(new InputStreamReader(valuesFileURL.openStream()));
260 }
261
262 /**
263 * Closes <code>valuesFileURL</code> after use in REPLAY_MODE.
264 *
265 * @throws IOException if an error occurs closing the file
266 */
267 public void closeReplayFile() throws IOException {
268 if (filePointer != null) {
269 filePointer.close();
270 filePointer = null;
271 }
272 }
273
274 /** Getter for property mu.
275 * @return Value of property mu.
276 */
277 public double getMu() {
278 return mu;
279 }
280
281 /** Setter for property mu.
282 * @param mu New value of property mu.
283 */
284 public void setMu(double mu) {
285 this.mu = mu;
286 }
287
288 /** Getter for property sigma.
289 * @return Value of property sigma.
290 */
291 public double getSigma() {
292 return sigma;
293 }
294
295 /** Setter for property sigma.
296 * @param sigma New value of property sigma.
297 */
298 public void setSigma(double sigma) {
299 this.sigma = sigma;
300 }
301
302 //------------- private methods ---------------------------------
303
304 /**
305 * Gets a random value in DIGEST_MODE.
306 * <p>
307 * <strong>Preconditions</strong>: <ul>
308 * <li>Before this method is called, <code>computeDistribution()</code>
309 * must have completed successfully; otherwise an
310 * <code>IllegalStateException</code> will be thrown</li></ul></p>
311 *
312 * @return next random value from the empirical distribution digest
313 */
314 private double getNextDigest() {
315 if ((empiricalDistribution == null) ||
316 (empiricalDistribution.getBinStats().size() == 0)) {
317 throw MathRuntimeException.createIllegalStateException(LocalizedFormats.DIGEST_NOT_INITIALIZED);
318 }
319 return empiricalDistribution.getNextValue();
320 }
321
322 /**
323 * Gets next sequential value from the <code>valuesFileURL</code>.
324 * <p>
325 * Throws an IOException if the read fails.</p>
326 * <p>
327 * This method will open the <code>valuesFileURL</code> if there is no
328 * replay file open.</p>
329 * <p>
330 * The <code>valuesFileURL</code> will be closed and reopened to wrap around
331 * from EOF to BOF if EOF is encountered. EOFException (which is a kind of
332 * IOException) may still be thrown if the <code>valuesFileURL</code> is
333 * empty.</p>
334 *
335 * @return next value from the replay file
336 * @throws IOException if there is a problem reading from the file
337 * @throws NumberFormatException if an invalid numeric string is
338 * encountered in the file
339 */
340 private double getNextReplay() throws IOException {
341 String str = null;
342 if (filePointer == null) {
343 resetReplayFile();
344 }
345 if ((str = filePointer.readLine()) == null) {
346 // we have probably reached end of file, wrap around from EOF to BOF
347 closeReplayFile();
348 resetReplayFile();
349 if ((str = filePointer.readLine()) == null) {
350 throw MathRuntimeException.createEOFException(LocalizedFormats.URL_CONTAINS_NO_DATA,
351 valuesFileURL);
352 }
353 }
354 return Double.valueOf(str).doubleValue();
355 }
356
357 /**
358 * Gets a uniformly distributed random value with mean = mu.
359 *
360 * @return random uniform value
361 */
362 private double getNextUniform() {
363 return randomData.nextUniform(0, 2 * mu);
364 }
365
366 /**
367 * Gets an exponentially distributed random value with mean = mu.
368 *
369 * @return random exponential value
370 */
371 private double getNextExponential() {
372 return randomData.nextExponential(mu);
373 }
374
375 /**
376 * Gets a Gaussian distributed random value with mean = mu
377 * and standard deviation = sigma.
378 *
379 * @return random Gaussian value
380 */
381 private double getNextGaussian() {
382 return randomData.nextGaussian(mu, sigma);
383 }
384
385}