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 = μ. */ 58 public static final int UNIFORM_MODE = 2; 59 60 /** Exponential random deviates with mean = μ. */ 61 public static final int EXPONENTIAL_MODE = 3; 62 63 /** Gaussian random deviates with mean = μ, std dev = σ. */ 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 */ 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 */ ValueServer(RandomData randomData)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 */ getNext()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 */ fill(double[] values)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 */ fill(int length)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 */ computeDistribution()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 */ computeDistribution(int binCount)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 */ getMode()202 public int getMode() { 203 return mode; 204 } 205 206 /** Setter for property mode. 207 * @param mode New value of property mode. 208 */ setMode(int mode)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 */ getValuesFileURL()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 */ setValuesFileURL(String url)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 */ setValuesFileURL(URL url)234 public void setValuesFileURL(URL url) { 235 this.valuesFileURL = url; 236 } 237 238 /** Getter for property empiricalDistribution. 239 * @return Value of property empiricalDistribution. 240 */ getEmpiricalDistribution()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 */ resetReplayFile()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 */ closeReplayFile()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 */ getMu()277 public double getMu() { 278 return mu; 279 } 280 281 /** Setter for property mu. 282 * @param mu New value of property mu. 283 */ setMu(double mu)284 public void setMu(double mu) { 285 this.mu = mu; 286 } 287 288 /** Getter for property sigma. 289 * @return Value of property sigma. 290 */ getSigma()291 public double getSigma() { 292 return sigma; 293 } 294 295 /** Setter for property sigma. 296 * @param sigma New value of property sigma. 297 */ setSigma(double sigma)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 */ getNextDigest()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 */ getNextReplay()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 */ getNextUniform()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 */ getNextExponential()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 */ getNextGaussian()381 private double getNextGaussian() { 382 return randomData.nextGaussian(mu, sigma); 383 } 384 385 } 386