001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.commons.math.random; 019 import java.io.BufferedReader; 020 import java.io.IOException; 021 import java.io.InputStreamReader; 022 import java.net.MalformedURLException; 023 import java.net.URL; 024 025 import org.apache.commons.math.MathRuntimeException; 026 027 /** 028 * Generates values for use in simulation applications. 029 * <p> 030 * How values are generated is determined by the <code>mode</code> 031 * property.</p> 032 * <p> 033 * Supported <code>mode</code> values are: <ul> 034 * <li> DIGEST_MODE -- uses an empirical distribution </li> 035 * <li> REPLAY_MODE -- replays data from <code>valuesFileURL</code></li> 036 * <li> UNIFORM_MODE -- generates uniformly distributed random values with 037 * mean = <code>mu</code> </li> 038 * <li> EXPONENTIAL_MODE -- generates exponentially distributed random values 039 * with mean = <code>mu</code></li> 040 * <li> GAUSSIAN_MODE -- generates Gaussian distributed random values with 041 * mean = <code>mu</code> and 042 * standard deviation = <code>sigma</code></li> 043 * <li> CONSTANT_MODE -- returns <code>mu</code> every time.</li></ul></p> 044 * 045 * @version $Revision: 811827 $ $Date: 2009-09-06 11:32:50 -0400 (Sun, 06 Sep 2009) $ 046 * 047 */ 048 public class ValueServer { 049 050 /** Use empirical distribution. */ 051 public static final int DIGEST_MODE = 0; 052 053 /** Replay data from valuesFilePath. */ 054 public static final int REPLAY_MODE = 1; 055 056 /** Uniform random deviates with mean = μ. */ 057 public static final int UNIFORM_MODE = 2; 058 059 /** Exponential random deviates with mean = μ. */ 060 public static final int EXPONENTIAL_MODE = 3; 061 062 /** Gaussian random deviates with mean = μ, std dev = σ. */ 063 public static final int GAUSSIAN_MODE = 4; 064 065 /** Always return mu */ 066 public static final int CONSTANT_MODE = 5; 067 068 /** mode determines how values are generated. */ 069 private int mode = 5; 070 071 /** URI to raw data values. */ 072 private URL valuesFileURL = null; 073 074 /** Mean for use with non-data-driven modes. */ 075 private double mu = 0.0; 076 077 /** Standard deviation for use with GAUSSIAN_MODE. */ 078 private double sigma = 0.0; 079 080 /** Empirical probability distribution for use with DIGEST_MODE. */ 081 private EmpiricalDistribution empiricalDistribution = null; 082 083 /** File pointer for REPLAY_MODE. */ 084 private BufferedReader filePointer = null; 085 086 /** RandomDataImpl to use for random data generation. */ 087 private RandomData randomData = new RandomDataImpl(); 088 089 // Data generation modes ====================================== 090 091 /** Creates new ValueServer */ 092 public ValueServer() { 093 } 094 095 /** 096 * Construct a ValueServer instance using a RandomData as its source 097 * of random data. 098 * 099 * @param randomData the RandomData instance used to source random data 100 * @since 1.1 101 */ 102 public ValueServer(RandomData randomData) { 103 this.randomData = randomData; 104 } 105 106 /** 107 * Returns the next generated value, generated according 108 * to the mode value (see MODE constants). 109 * 110 * @return generated value 111 * @throws IOException in REPLAY_MODE if a file I/O error occurs 112 */ 113 public double getNext() throws IOException { 114 switch (mode) { 115 case DIGEST_MODE: return getNextDigest(); 116 case REPLAY_MODE: return getNextReplay(); 117 case UNIFORM_MODE: return getNextUniform(); 118 case EXPONENTIAL_MODE: return getNextExponential(); 119 case GAUSSIAN_MODE: return getNextGaussian(); 120 case CONSTANT_MODE: return mu; 121 default: throw MathRuntimeException.createIllegalStateException( 122 "unknown mode {0}, known modes: " + 123 "{1} ({2}), {3} ({4}), {5} ({6}), {7} ({8}), {9} ({10}) and {11} ({12})", 124 mode, 125 "DIGEST_MODE", DIGEST_MODE, "REPLAY_MODE", REPLAY_MODE, 126 "UNIFORM_MODE", UNIFORM_MODE, "EXPONENTIAL_MODE", EXPONENTIAL_MODE, 127 "GAUSSIAN_MODE", GAUSSIAN_MODE, "CONSTANT_MODE", CONSTANT_MODE); 128 } 129 } 130 131 /** 132 * Fills the input array with values generated using getNext() repeatedly. 133 * 134 * @param values array to be filled 135 * @throws IOException in REPLAY_MODE if a file I/O error occurs 136 */ 137 public void fill(double[] values) throws IOException { 138 for (int i = 0; i < values.length; i++) { 139 values[i] = getNext(); 140 } 141 } 142 143 /** 144 * Returns an array of length <code>length</code> with values generated 145 * using getNext() repeatedly. 146 * 147 * @param length length of output array 148 * @return array of generated values 149 * @throws IOException in REPLAY_MODE if a file I/O error occurs 150 */ 151 public double[] fill(int length) throws IOException { 152 double[] out = new double[length]; 153 for (int i = 0; i < length; i++) { 154 out[i] = getNext(); 155 } 156 return out; 157 } 158 159 /** 160 * Computes the empirical distribution using values from the file 161 * in <code>valuesFileURL</code>, using the default number of bins. 162 * <p> 163 * <code>valuesFileURL</code> must exist and be 164 * readable by *this at runtime.</p> 165 * <p> 166 * This method must be called before using <code>getNext()</code> 167 * with <code>mode = DIGEST_MODE</code></p> 168 * 169 * @throws IOException if an I/O error occurs reading the input file 170 */ 171 public void computeDistribution() throws IOException { 172 empiricalDistribution = new EmpiricalDistributionImpl(); 173 empiricalDistribution.load(valuesFileURL); 174 } 175 176 /** 177 * Computes the empirical distribution using values from the file 178 * in <code>valuesFileURL</code> and <code>binCount</code> bins. 179 * <p> 180 * <code>valuesFileURL</code> must exist and be readable by this process 181 * at runtime.</p> 182 * <p> 183 * This method must be called before using <code>getNext()</code> 184 * with <code>mode = DIGEST_MODE</code></p> 185 * 186 * @param binCount the number of bins used in computing the empirical 187 * distribution 188 * @throws IOException if an error occurs reading the input file 189 */ 190 public void computeDistribution(int binCount) 191 throws IOException { 192 empiricalDistribution = new EmpiricalDistributionImpl(binCount); 193 empiricalDistribution.load(valuesFileURL); 194 mu = empiricalDistribution.getSampleStats().getMean(); 195 sigma = empiricalDistribution.getSampleStats().getStandardDeviation(); 196 } 197 198 /** Getter for property mode. 199 * @return Value of property mode. 200 */ 201 public int getMode() { 202 return mode; 203 } 204 205 /** Setter for property mode. 206 * @param mode New value of property mode. 207 */ 208 public void setMode(int mode) { 209 this.mode = mode; 210 } 211 212 /** 213 * Getter for <code>valuesFileURL<code> 214 * @return Value of property valuesFileURL. 215 */ 216 public URL getValuesFileURL() { 217 return valuesFileURL; 218 } 219 220 /** 221 * Sets the <code>valuesFileURL</code> using a string URL representation 222 * @param url String representation for new valuesFileURL. 223 * @throws MalformedURLException if url is not well formed 224 */ 225 public void setValuesFileURL(String url) throws MalformedURLException { 226 this.valuesFileURL = new URL(url); 227 } 228 229 /** 230 * Sets the <code>valuesFileURL</code> 231 * @param url New value of property valuesFileURL. 232 */ 233 public void setValuesFileURL(URL url) { 234 this.valuesFileURL = url; 235 } 236 237 /** Getter for property empiricalDistribution. 238 * @return Value of property empiricalDistribution. 239 */ 240 public EmpiricalDistribution getEmpiricalDistribution() { 241 return empiricalDistribution; 242 } 243 244 /** 245 * Resets REPLAY_MODE file pointer to the beginning of the <code>valuesFileURL</code>. 246 * 247 * @throws IOException if an error occurs opening the file 248 */ 249 public void resetReplayFile() throws IOException { 250 if (filePointer != null) { 251 try { 252 filePointer.close(); 253 filePointer = null; 254 } catch (IOException ex) { 255 // ignore 256 } 257 } 258 filePointer = new BufferedReader(new InputStreamReader(valuesFileURL.openStream())); 259 } 260 261 /** 262 * Closes <code>valuesFileURL</code> after use in REPLAY_MODE. 263 * 264 * @throws IOException if an error occurs closing the file 265 */ 266 public void closeReplayFile() throws IOException { 267 if (filePointer != null) { 268 filePointer.close(); 269 filePointer = null; 270 } 271 } 272 273 /** Getter for property mu. 274 * @return Value of property mu. 275 */ 276 public double getMu() { 277 return mu; 278 } 279 280 /** Setter for property mu. 281 * @param mu New value of property mu. 282 */ 283 public void setMu(double mu) { 284 this.mu = mu; 285 } 286 287 /** Getter for property sigma. 288 * @return Value of property sigma. 289 */ 290 public double getSigma() { 291 return sigma; 292 } 293 294 /** Setter for property sigma. 295 * @param sigma New value of property sigma. 296 */ 297 public void setSigma(double sigma) { 298 this.sigma = sigma; 299 } 300 301 //------------- private methods --------------------------------- 302 303 /** 304 * Gets a random value in DIGEST_MODE. 305 * <p> 306 * <strong>Preconditions</strong>: <ul> 307 * <li>Before this method is called, <code>computeDistribution()</code> 308 * must have completed successfully; otherwise an 309 * <code>IllegalStateException</code> will be thrown</li></ul></p> 310 * 311 * @return next random value from the empirical distribution digest 312 */ 313 private double getNextDigest() { 314 if ((empiricalDistribution == null) || 315 (empiricalDistribution.getBinStats().size() == 0)) { 316 throw MathRuntimeException.createIllegalStateException("digest not initialized"); 317 } 318 return empiricalDistribution.getNextValue(); 319 } 320 321 /** 322 * Gets next sequential value from the <code>valuesFileURL</code>. 323 * <p> 324 * Throws an IOException if the read fails.</p> 325 * <p> 326 * This method will open the <code>valuesFileURL</code> if there is no 327 * replay file open.</p> 328 * <p> 329 * The <code>valuesFileURL</code> will be closed and reopened to wrap around 330 * from EOF to BOF if EOF is encountered. EOFException (which is a kind of 331 * IOException) may still be thrown if the <code>valuesFileURL</code> is 332 * empty.</p> 333 * 334 * @return next value from the replay file 335 * @throws IOException if there is a problem reading from the file 336 * @throws NumberFormatException if an invalid numeric string is 337 * encountered in the file 338 */ 339 private double getNextReplay() throws IOException { 340 String str = null; 341 if (filePointer == null) { 342 resetReplayFile(); 343 } 344 if ((str = filePointer.readLine()) == null) { 345 // we have probably reached end of file, wrap around from EOF to BOF 346 closeReplayFile(); 347 resetReplayFile(); 348 if ((str = filePointer.readLine()) == null) { 349 throw MathRuntimeException.createEOFException("URL {0} contains no data", 350 valuesFileURL); 351 } 352 } 353 return Double.valueOf(str).doubleValue(); 354 } 355 356 /** 357 * Gets a uniformly distributed random value with mean = mu. 358 * 359 * @return random uniform value 360 */ 361 private double getNextUniform() { 362 return randomData.nextUniform(0, 2 * mu); 363 } 364 365 /** 366 * Gets an exponentially distributed random value with mean = mu. 367 * 368 * @return random exponential value 369 */ 370 private double getNextExponential() { 371 return randomData.nextExponential(mu); 372 } 373 374 /** 375 * Gets a Gaussian distributed random value with mean = mu 376 * and standard deviation = sigma. 377 * 378 * @return random Gaussian value 379 */ 380 private double getNextGaussian() { 381 return randomData.nextGaussian(mu, sigma); 382 } 383 384 }