View Javadoc

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