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 }