| 1 | package desmoj.core.dist; |
| 2 | |
| 3 | import desmoj.core.simulator.Model; |
| 4 | |
| 5 | /** |
| 6 | * Base class for all pseudo random number distributions used in this package. |
| 7 | * Defines a set of methods usefull for all kinds of random distributions that |
| 8 | * can be based upon a stream of uniform distributed pseudo random numbers. |
| 9 | * Prefabricated distributions implemented in this package can handle uniform, |
| 10 | * normal (gaussian), bernoulli, poisson and heuristic distributions with return |
| 11 | * values of the primitive data types double (floating point), long (integer) |
| 12 | * and boolean (true or false). Inherit from this class if you want to implement |
| 13 | * new types of distributions handing back values of other types than those |
| 14 | * listed above. Basic idea is to use a pseudo random generator which produces a |
| 15 | * uniformly distributed stream of double numbers between 0 and 1 use inverse |
| 16 | * transformation to generate the desired distribution. See also [Page91, p. |
| 17 | * 107] Note that although this class implements all methods, it is set to be |
| 18 | * abstract, since instantiating this class would not produce any meaningfull |
| 19 | * distribution to be used by a client. |
| 20 | * |
| 21 | * @see desmoj.core.dist.UniformRandomGenerator |
| 22 | * @see desmoj.core.dist.LinearCongruentialRandomGenerator |
| 23 | * |
| 24 | * @version DESMO-J, Ver. 2.3.3 copyright (c) 2011 |
| 25 | * @author Tim Lechler |
| 26 | * |
| 27 | * Licensed under the Apache License, Version 2.0 (the "License"); you |
| 28 | * may not use this file except in compliance with the License. You may |
| 29 | * obtain a copy of the License at |
| 30 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 31 | * |
| 32 | * Unless required by applicable law or agreed to in writing, software |
| 33 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 34 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| 35 | * implied. See the License for the specific language governing |
| 36 | * permissions and limitations under the License. |
| 37 | * |
| 38 | */ |
| 39 | public abstract class Distribution extends desmoj.core.simulator.Reportable { |
| 40 | |
| 41 | /** |
| 42 | * The underlying uniform pseudo random generator available to every |
| 43 | * distribution inheriting from this abstract class. Valid generators have |
| 44 | * to implement the <code>desmoj.dist.UniformRandomGenerator</code> |
| 45 | * interface. By default the <code>desmoj.dist.DefaultRandomGenerator</code> |
| 46 | * is used. |
| 47 | * |
| 48 | * @see desmoj.core.dist.UniformRandomGenerator |
| 49 | * @see desmoj.core.dist.LinearCongruentialRandomGenerator |
| 50 | */ |
| 51 | protected desmoj.core.dist.UniformRandomGenerator randomGenerator; |
| 52 | |
| 53 | /** |
| 54 | * The status of the random number generation. If set to true, antithetic |
| 55 | * values are delivered. These depend upon the kind of distribution, so this |
| 56 | * value here will probably be most useful to switch the algorithm in the |
| 57 | * implementation of the abstract <code>sample()</code> method between |
| 58 | * "normal" and "antithetic" value generation. This feature is not |
| 59 | * associated to the pseudo random generator since the algorithm for |
| 60 | * calculating antithetic values might not require antithetic uniformly |
| 61 | * distributed values. |
| 62 | */ |
| 63 | protected boolean antithetic; |
| 64 | |
| 65 | /** |
| 66 | * The seed of the underlying pseudorandom generator. The seed value is |
| 67 | * passed on to the underlying <code>UniformRandomGenerator</code> but since |
| 68 | * those generators are not supposed to keep track of their initial seed |
| 69 | * value it is stored here to make sure they are not lost. |
| 70 | */ |
| 71 | protected long initialSeed; |
| 72 | |
| 73 | /** |
| 74 | * This flag shows, if a distribution may produce negative samples or not. |
| 75 | * This is important, if the value of a distribution's sample is to be used |
| 76 | * for creating a TimeSpan object, which allows positive values only. If |
| 77 | * this switch is set to <code>true</code>, the distribution will only |
| 78 | * return positive samples. If a negative sample is drawn, it will be |
| 79 | * dismissed and new samples will be drawn until a positive is produced, |
| 80 | * which will be returned. |
| 81 | */ |
| 82 | protected boolean nonNegative; |
| 83 | |
| 84 | /** |
| 85 | * Creates a RandomDistribution object which gets its initial seed from the |
| 86 | * experiment's seedgenerator. The |
| 87 | * <code>LinearCongruentialRandomGenerator</code> is used as the underlying |
| 88 | * uniform pseudo random number generator for all pseudo random distribution |
| 89 | * . |
| 90 | * |
| 91 | * @param owner |
| 92 | * Model : The distribution's owner |
| 93 | * @param name |
| 94 | * java.lang.String : The distribution's name |
| 95 | * @param showInReport |
| 96 | * boolean : Flag to show distribution in report |
| 97 | * @param showInTrace |
| 98 | * boolean : Flag to show distribution in trace |
| 99 | */ |
| 100 | public Distribution(Model owner, String name, boolean showInReport, |
| 101 | boolean showInTrace) { |
| 102 | |
| 103 | super(owner, name, showInReport, showInTrace); // construct the |
| 104 | // reportable |
| 105 | if (randomGenerator == null) { |
| 106 | try { |
| 107 | randomGenerator = owner.getExperiment() |
| 108 | .getDistributionManager().getRandomNumberGenerator() |
| 109 | .newInstance(); // default RandomGenerator |
| 110 | } catch (InstantiationException e) { |
| 111 | randomGenerator = new LinearCongruentialRandomGenerator(); |
| 112 | } catch (IllegalAccessException e) { |
| 113 | randomGenerator = new LinearCongruentialRandomGenerator(); |
| 114 | } |
| 115 | } |
| 116 | owner.getExperiment().getDistributionManager().register(this); |
| 117 | |
| 118 | // set seed in case experiment running |
| 119 | // (for not yet running experiments, this happens automatically |
| 120 | // when the experiment is started) |
| 121 | if (owner.getExperiment().isRunning()) { |
| 122 | randomGenerator.setSeed(initialSeed); |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | /** |
| 127 | * Changes the underlying random generator to the one given as a parameter. |
| 128 | * Custom random generators have to implement the |
| 129 | * desmoj.dist.UniormRandomGenerator interface. Note that changing the |
| 130 | * underlying random generator forces a reset, since a new generator might |
| 131 | * produce a completely different stream of pseudo random numbers that won't |
| 132 | * enable us to reproduce the stream of numbers probably delivered by the |
| 133 | * previously used generator. |
| 134 | * |
| 135 | * @param randomGenerator |
| 136 | * java.util.Random : the random generator used for creating |
| 137 | * distributions |
| 138 | */ |
| 139 | public void changeRandomGenerator( |
| 140 | desmoj.core.dist.UniformRandomGenerator randomGenerator) { |
| 141 | |
| 142 | this.randomGenerator = randomGenerator; |
| 143 | reset(); |
| 144 | |
| 145 | } |
| 146 | |
| 147 | /** |
| 148 | * Creates the default reporter associated with this distribution. The basic |
| 149 | * <code>DistributionReporter</code> returned as a default implementation of |
| 150 | * this method simply reports the distribution's name, number of |
| 151 | * observations (samples given), seed and point of simulation time of the |
| 152 | * last reset. |
| 153 | * |
| 154 | * @return Reportable : The reporter associated with this distribution |
| 155 | * @see desmoj.core.report.DistributionReporter |
| 156 | */ |
| 157 | public desmoj.core.report.Reporter createReporter() { |
| 158 | |
| 159 | return new desmoj.core.report.DistributionReporter(this); |
| 160 | |
| 161 | } |
| 162 | |
| 163 | /** |
| 164 | * Returns the seed value since last reset. |
| 165 | * |
| 166 | * @return long : The initial seed value |
| 167 | */ |
| 168 | public long getInitialSeed() { |
| 169 | |
| 170 | return initialSeed; |
| 171 | |
| 172 | } |
| 173 | |
| 174 | /** |
| 175 | * Tells if this distribution can return negative samples. |
| 176 | * |
| 177 | * @return boolean : If <code>true</code> it returns positive samples only |
| 178 | */ |
| 179 | public boolean getNonNegative() { |
| 180 | |
| 181 | return nonNegative; |
| 182 | |
| 183 | } |
| 184 | |
| 185 | /** |
| 186 | * Returns the number of Samples given by this distribution. The number of |
| 187 | * samples is increased whenever the sample() method is called. It is based |
| 188 | * on the random numbers of the distribution, not on the number of random |
| 189 | * numbers produced by the underlying random generator, since some |
| 190 | * distributions use algorithms consuming more than one uniformly |
| 191 | * distributed random number to produce one sample following the desired |
| 192 | * distribution. |
| 193 | * |
| 194 | * @return long : the number of samples given to clients |
| 195 | */ |
| 196 | public long getNumSamples() { |
| 197 | |
| 198 | return getObservations(); |
| 199 | |
| 200 | } |
| 201 | |
| 202 | /** |
| 203 | * Returns the current status for antithetic random number generation in |
| 204 | * this distribution. |
| 205 | * |
| 206 | * @return boolean : The status of antithetic pseudo random number |
| 207 | * generation |
| 208 | * @see desmoj.core.dist.Distribution#setAntithetic |
| 209 | */ |
| 210 | public boolean isAntithetic() { |
| 211 | |
| 212 | return antithetic; |
| 213 | |
| 214 | } |
| 215 | |
| 216 | /** |
| 217 | * Resets the pseudo random generator's seed and the number of samples given |
| 218 | * to zero. The field antithetic keeps the value it has had before the |
| 219 | * reset. |
| 220 | */ |
| 221 | public void reset() { |
| 222 | |
| 223 | if (randomGenerator == null) { |
| 224 | try { |
| 225 | randomGenerator = this.getModel().getExperiment() |
| 226 | .getDistributionManager().getRandomNumberGenerator() |
| 227 | .newInstance(); // default RandomGenerator |
| 228 | } catch (InstantiationException e) { |
| 229 | randomGenerator = new LinearCongruentialRandomGenerator(); |
| 230 | } catch (IllegalAccessException e) { |
| 231 | randomGenerator = new LinearCongruentialRandomGenerator(); |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | // sets seed to the seed specified in constructor or by call to |
| 236 | // setSeed(long) |
| 237 | randomGenerator.setSeed(initialSeed); // initialSeed stays unchanged |
| 238 | // here |
| 239 | |
| 240 | // antithetic = false; |
| 241 | /* |
| 242 | * no need to change this to false. If this distribution has delivered |
| 243 | * antithetic random number than it will do so after the reset, too. |
| 244 | */ |
| 245 | |
| 246 | super.reset(); // reset the Reportable, too. |
| 247 | } |
| 248 | |
| 249 | /** |
| 250 | * Resets the pseudo random generator's seed to the value passed, the number |
| 251 | * of samples given to zero and sets antithetic to false for this |
| 252 | * distribution. Acts the same as a call of method <code>reset()</code> and |
| 253 | * a consecutive call to <code>setSeed(long)</code>. |
| 254 | * |
| 255 | * @param newSeed |
| 256 | * long : new seed to be used by underlying random number |
| 257 | * generator after reset |
| 258 | */ |
| 259 | public void reset(long newSeed) { |
| 260 | |
| 261 | randomGenerator.setSeed(newSeed); |
| 262 | |
| 263 | this.initialSeed = newSeed; // initialSeed is changed here |
| 264 | |
| 265 | // antithetic = false; |
| 266 | /* |
| 267 | * no need to change this to false. If this distribution has delivered |
| 268 | * antithetic random number than it will do so after the reset, too. |
| 269 | */ |
| 270 | |
| 271 | super.reset(); // reset the Reportable, too. |
| 272 | |
| 273 | } |
| 274 | |
| 275 | /** |
| 276 | * Convenience method to return the distribution's sample as <code>Object</code>. |
| 277 | * For type safety, method <code>sample()</code> should be preferred. However, |
| 278 | * this method is useful for environments requiring a non-genetic access point |
| 279 | * to obtain samples from any distribution. |
| 280 | * |
| 281 | * @return Object : A sample from this this distribution wrapped as <code>Object</code>. |
| 282 | */ |
| 283 | public abstract Object sampleObject(); |
| 284 | |
| 285 | /** |
| 286 | * Switches this distribution to produce antithetic samples. To obtain |
| 287 | * antithetic random numbers, call this method with the parameter |
| 288 | * <code>true</code>. Antithetic random numbers are used to minimize the |
| 289 | * standard deviation of a series of simulation runs. The results of a run |
| 290 | * with normal random numbers has to be standardized with the results of a |
| 291 | * run using antithetic random numbers, thus doubling the number of samples |
| 292 | * needed, but also lowering the standard deviation of the results of that |
| 293 | * simulation. See [Page91, p.139]. |
| 294 | * |
| 295 | * @param newAntiStatus |
| 296 | * boolean : Parameter <code>true</code> switches antithetic mode |
| 297 | * on, <code>false</code> switches antithetic mode off |
| 298 | */ |
| 299 | public void setAntithetic(boolean newAntiStatus) { |
| 300 | |
| 301 | antithetic = newAntiStatus; |
| 302 | reset(); |
| 303 | |
| 304 | } |
| 305 | |
| 306 | /** |
| 307 | * Sets the nonNegative switch to the given value. If nonNegative is set to |
| 308 | * <code>true</code> the distribution returns positive samples only, |
| 309 | * otherwise it also produces negative samples, if possible. |
| 310 | * |
| 311 | * @param newValue |
| 312 | * boolean : If <code>true</code> the distribution is set to |
| 313 | * return positive samples only, otherwise it also produces |
| 314 | * negative samples, if possible. |
| 315 | */ |
| 316 | public void setNonNegative(boolean newValue) { |
| 317 | this.nonNegative = newValue; |
| 318 | } |
| 319 | |
| 320 | /** |
| 321 | * Sets the underlying pseudo random number generator's seed to the value |
| 322 | * given. The seed controls the starting value of the random generators and |
| 323 | * all following generated pseudo random numbers. Resetting the seed between |
| 324 | * two simulation runs will let you use identical streams of random numbers. |
| 325 | * That will enable you to compare different strategies within your model |
| 326 | * based on the same random number stream produced by the random generator. |
| 327 | * |
| 328 | * @param newSeed |
| 329 | * long : new seed used by underlying pseudo random number |
| 330 | * generator |
| 331 | */ |
| 332 | public void setSeed(long newSeed) { |
| 333 | |
| 334 | randomGenerator.setSeed(newSeed); // well, the seed is passed on... |
| 335 | // ;-) |
| 336 | initialSeed = newSeed; // remember new seed for next reset() |
| 337 | reset(); // and do a reset of statistics to display when a new seed |
| 338 | // was |
| 339 | // set |
| 340 | |
| 341 | } |
| 342 | |
| 343 | /** |
| 344 | * Generates the trace output of each sample. This method is called by |
| 345 | * sample(). |
| 346 | * |
| 347 | * @param sample |
| 348 | * String : The last sample, converted to a String |
| 349 | */ |
| 350 | protected void traceLastSample(String sample) { |
| 351 | |
| 352 | if (this.currentlySendTraceNotes()) |
| 353 | this.sendTraceNote("samples " + sample + " from " + this.getName()); |
| 354 | |
| 355 | } |
| 356 | } |