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 | } |