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.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 }