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 }