Home | History | Annotate | Download | only in random
      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 import 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  */
     49 public 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 }
    386