1 | package de.uka.ipd.sdq.simucomframework; |
2 | |
3 | import java.util.Random; |
4 | import java.util.concurrent.LinkedBlockingQueue; |
5 | |
6 | import org.apache.log4j.Logger; |
7 | |
8 | import de.uka.ipd.sdq.probfunction.math.IRandomGenerator; |
9 | |
10 | import de.uka.ipd.sdq.probfunction.math.apache.impl.MT19937RandomGenerator; |
11 | import de.uka.ipd.sdq.probfunction.math.random.IRandomStream; |
12 | import de.uka.ipd.sdq.simucomframework.variables.functions.LogNormDistFunction; |
13 | import de.uka.ipd.sdq.simucomframework.variables.functions.NormDistFunction; |
14 | |
15 | /** |
16 | * SimuComDefaultRandomNumberGenerator generates random numbers in a separate thread. |
17 | * SimuComDefaultRandomNumberGenerator currently implements the interface for |
18 | * random numbers needed by the prob function package. However, internally it |
19 | * relies on a random stream implementing the RandomStream interface from JSS. |
20 | * |
21 | * SimuComDefaultRandomNumberGenerator implements {@link RandomStream} as the |
22 | * functions like {@link LogNormDistFunction} and {@link NormDistFunction} in |
23 | * package de.uka.ipd.sdq.simucomframework.variables.functions. |
24 | * |
25 | * This class is optimized for multi-cores, thus speeding up simulation significantly for |
26 | * at least two cores. Here everything related to random numbers in SimuCom should be then localised, especially seed |
27 | * initialisation and the multi-threading stuff (which was - as always - deadlock |
28 | * prone while implementing). Also it locates the dependency to the [0..1] random |
29 | * number generator to a single place in the source (remember this needs to be |
30 | * changed to get away from GPL code...). |
31 | * |
32 | * @author Steffen Becker |
33 | * |
34 | */ |
35 | public class SimuComDefaultRandomNumberGenerator implements IRandomGenerator { |
36 | |
37 | private static final int MAX_PRODUCER_BUFFER_SIZE = 1000; |
38 | private static final Logger logger = |
39 | Logger.getLogger(SimuComDefaultRandomNumberGenerator.class.getName()); |
40 | private static long streamCounter = 0; |
41 | private long myID; |
42 | private final IRandomStream rndNumberGenerator; |
43 | private LinkedBlockingQueue<Double> producerQueue = new LinkedBlockingQueue<Double>(MAX_PRODUCER_BUFFER_SIZE); |
44 | private Thread producerThread; |
45 | private volatile boolean finished; |
46 | |
47 | public SimuComDefaultRandomNumberGenerator(long[] randomSeed){ |
48 | this.myID = streamCounter++; |
49 | this.finished = false; |
50 | |
51 | if (logger.isDebugEnabled()) { |
52 | logger.debug("Initialising random number generator! [Stream ID = "+myID+"]"); |
53 | } |
54 | rndNumberGenerator = new MT19937RandomGenerator();//new MRG32k3aRandomGenerator(); |
55 | initRandomSeed(randomSeed); |
56 | |
57 | this.producerThread = new Thread(new Runnable(){ |
58 | public void run() { |
59 | logger.debug("Random number producer started! [Stream ID = "+myID+"]"); |
60 | try { |
61 | while (!finished) { |
62 | producerQueue.put(rndNumberGenerator.nextDouble()); |
63 | } |
64 | } catch (InterruptedException e) { |
65 | logger.debug("Bad concurrency problem",e); |
66 | throw new RuntimeException("Unexpected interruption of concurrent threads"); |
67 | } |
68 | logger.debug("Random number producer stopped! [Stream ID = "+myID+"]"); |
69 | } |
70 | }); |
71 | producerThread.start(); |
72 | |
73 | if (logger.isDebugEnabled()) { |
74 | logger.debug("Random number stream initialised! [Stream ID = "+myID+"]"); |
75 | } |
76 | } |
77 | |
78 | /** |
79 | * Set up seed of random number generator |
80 | * @param randomSeed The random number generator's seed. May be null for default initialisation |
81 | */ |
82 | private void initRandomSeed(long[] randomSeed) { |
83 | if (randomSeed != null) { |
84 | if (randomSeed.length == 6) { |
85 | if (logger.isDebugEnabled()) { |
86 | logger.debug("Setting fixed seed for random number generator! [Stream ID = "+myID+"]"); |
87 | for (int i=0; i<6; i++) { |
88 | logger.debug("Seed "+i+" = "+randomSeed[i]); |
89 | } |
90 | } |
91 | rndNumberGenerator.setSeed(randomSeed); |
92 | } |
93 | else { |
94 | throw new RuntimeException("Seed array must have length of six longs for initialising random number generator"); |
95 | } |
96 | } |
97 | else { |
98 | if (logger.isDebugEnabled()) { |
99 | logger.debug("Setting random seed for random number generator! [Stream ID = "+myID+"]"); |
100 | } |
101 | long[] myRandomSeed = new long[6]; |
102 | Random r = new Random(); |
103 | for (int i = 0; i < myRandomSeed.length; i++) { |
104 | myRandomSeed[i] = r.nextInt(); |
105 | } |
106 | rndNumberGenerator.setSeed(myRandomSeed); |
107 | } |
108 | } |
109 | |
110 | /** |
111 | * Returns a (pseudo)random number from the uniform distribution over the interval (0, 1), using this stream, after advancing its state by one step. The generators programmed in SSJ never return the values 0 or 1. |
112 | * This method takes a previously generated random number from the asynchronously running generator thread. |
113 | */ |
114 | public double random() { |
115 | if (logger.isDebugEnabled()) { |
116 | logger.debug("Drawing a random number from random number stream ["+myID+"]!"); |
117 | } |
118 | double d; |
119 | try { |
120 | d = producerQueue.take(); |
121 | } catch (InterruptedException e) { |
122 | throw new RuntimeException("Unexpected interruption of concurrent threads"); |
123 | } |
124 | if (logger.isDebugEnabled()) { |
125 | logger.debug("Number is: "+d); |
126 | } |
127 | return d; |
128 | } |
129 | |
130 | public void dispose() { |
131 | finished = true; |
132 | producerQueue.clear(); |
133 | } |
134 | |
135 | /** |
136 | * Generates n (pseudo)random numbers from the uniform distribution and stores them into the array u starting at index start. |
137 | * (doc from {@link RandomStream}) |
138 | * @param u - array that will contain the generated uniforms |
139 | * @param start - starting index, in the array u, to write uniforms from |
140 | * @param n - number of uniforms to generate |
141 | */ |
142 | public void nextArrayOfDouble(double[] u, int start, int n) { |
143 | for (int i = start; i < u.length && i - start < n; i++) { |
144 | u[i] = this.random(); |
145 | } |
146 | } |
147 | |
148 | /** |
149 | * Generates n (pseudo)random numbers from the discrete uniform distribution over the |
150 | * integers {i, i + 1,..., j}, using this stream and stores the result in the |
151 | * array u starting at index start. (Calls nextInt n times.) |
152 | * (doc from {@link RandomStream} |
153 | * @param i - smallest integer that can be generated |
154 | * @param j - greatest integer that can be generated |
155 | * @param u - array that will contain the generated values |
156 | * @param start - starting index, in the array u, to write integers from |
157 | * @param n - number of values being generated |
158 | */ |
159 | public void nextArrayOfInt(int i, int j, int[] u, int start, |
160 | int n) { |
161 | for (int index = start; index < u.length && index - start < n; index++) { |
162 | u[index] = this.nextInt(i, j); |
163 | } |
164 | } |
165 | |
166 | /** |
167 | * Returns a (pseudo)random number from the uniform distribution over the interval (0, 1), using this stream, after advancing its state by one step. The generators programmed in SSJ never return the values 0 or 1. |
168 | * Calls random() which takes a previously generated random number from the asynchronously running generator thread. |
169 | */ |
170 | public double nextDouble() { |
171 | return this.random(); |
172 | } |
173 | |
174 | /** |
175 | * Returns a (pseudo)random number from the discrete uniform distribution over the integers {i, i + 1,..., j}, using this stream. (Calls nextDouble once.) |
176 | * Copied from {@link RandomStreamBase}. |
177 | * @param i - smallest integer that can be generated |
178 | * @param j - greatest integer that can be generated |
179 | */ |
180 | public int nextInt(int i, int j) { |
181 | // copied from RandomStreamBase |
182 | return i + (int)(nextDouble() * (j - i + 1.0)); |
183 | } |
184 | |
185 | /** |
186 | * does nothing because this implementation does not support any substreams |
187 | */ |
188 | public void resetNextSubstream() { |
189 | } |
190 | |
191 | /** |
192 | * does nothing because this implementation does not support any substreams |
193 | */ |
194 | public void resetStartStream() { |
195 | } |
196 | |
197 | |
198 | /** |
199 | * does nothing because this implementation does not support any substreams |
200 | */ |
201 | public void resetStartSubstream() { |
202 | } |
203 | |
204 | } |