samer@0
|
1 /***********************************************************************\
|
samer@0
|
2 *
|
samer@0
|
3 * File: RngStream.c for multiple streams of Random Numbers
|
samer@0
|
4 * Language: ANSI C
|
samer@0
|
5 * Copyright: Pierre L'Ecuyer, University of Montreal
|
samer@0
|
6 * Date: 14 August 2001
|
samer@0
|
7 * License: GPL version 2 or later
|
samer@0
|
8 *
|
samer@0
|
9 * Notice: Please contact P. L'Ecuyer at <lecuyer@iro.UMontreal.ca>
|
samer@0
|
10 * for commercial purposes.
|
samer@0
|
11 *
|
samer@0
|
12 \***********************************************************************/
|
samer@0
|
13
|
samer@0
|
14
|
samer@0
|
15 #include "RngStream.h"
|
samer@0
|
16 #include <stdio.h>
|
samer@0
|
17 #include <stdlib.h>
|
samer@0
|
18 #include <string.h>
|
samer@0
|
19 #include <math.h>
|
samer@0
|
20
|
samer@0
|
21 /*---------------------------------------------------------------------*/
|
samer@0
|
22 /* Private part. */
|
samer@0
|
23 /*---------------------------------------------------------------------*/
|
samer@0
|
24
|
samer@0
|
25 typedef struct rng_state *RngStream;
|
samer@0
|
26
|
samer@0
|
27 #define norm 2.328306549295727688e-10
|
samer@0
|
28 #define m1 4294967087.0
|
samer@0
|
29 #define m2 4294944443.0
|
samer@0
|
30 #define a12 1403580.0
|
samer@0
|
31 #define a13n 810728.0
|
samer@0
|
32 #define a21 527612.0
|
samer@0
|
33 #define a23n 1370589.0
|
samer@0
|
34
|
samer@0
|
35 #define two17 131072.0
|
samer@0
|
36 #define two53 9007199254740992.0
|
samer@0
|
37 #define fact 5.9604644775390625e-8 /* 1 / 2^24 */
|
samer@0
|
38
|
samer@0
|
39
|
samer@0
|
40
|
samer@0
|
41 /* Default initial seed */
|
samer@0
|
42 static double nextSeed[6] = { 12345, 12345, 12345, 12345, 12345, 12345 };
|
samer@0
|
43
|
samer@0
|
44
|
samer@0
|
45 /* The following are the transition matrices of the two MRG components */
|
samer@0
|
46 /* (in matrix form), raised to the powers 2^0, 2^76, and 2^127, resp.*/
|
samer@0
|
47
|
samer@0
|
48 static double A1p0[3][3] = {
|
samer@0
|
49 { 0.0, 1.0, 0.0 },
|
samer@0
|
50 { 0.0, 0.0, 1.0 },
|
samer@0
|
51 { -810728.0, 1403580.0, 0.0 }
|
samer@0
|
52 };
|
samer@0
|
53
|
samer@0
|
54 static double A2p0[3][3] = {
|
samer@0
|
55 { 0.0, 1.0, 0.0 },
|
samer@0
|
56 { 0.0, 0.0, 1.0 },
|
samer@0
|
57 { -1370589.0, 0.0, 527612.0 }
|
samer@0
|
58 };
|
samer@0
|
59
|
samer@0
|
60
|
samer@0
|
61 static double A1p76[3][3] = {
|
samer@0
|
62 { 82758667.0, 1871391091.0, 4127413238.0 },
|
samer@0
|
63 { 3672831523.0, 69195019.0, 1871391091.0 },
|
samer@0
|
64 { 3672091415.0, 3528743235.0, 69195019.0 }
|
samer@0
|
65 };
|
samer@0
|
66
|
samer@0
|
67 static double A2p76[3][3] = {
|
samer@0
|
68 { 1511326704.0, 3759209742.0, 1610795712.0 },
|
samer@0
|
69 { 4292754251.0, 1511326704.0, 3889917532.0 },
|
samer@0
|
70 { 3859662829.0, 4292754251.0, 3708466080.0 }
|
samer@0
|
71 };
|
samer@0
|
72
|
samer@0
|
73 static double A1p127[3][3] = {
|
samer@0
|
74 { 2427906178.0, 3580155704.0, 949770784.0 },
|
samer@0
|
75 { 226153695.0, 1230515664.0, 3580155704.0 },
|
samer@0
|
76 { 1988835001.0, 986791581.0, 1230515664.0 }
|
samer@0
|
77 };
|
samer@0
|
78
|
samer@0
|
79 static double A2p127[3][3] = {
|
samer@0
|
80 { 1464411153.0, 277697599.0, 1610723613.0 },
|
samer@0
|
81 { 32183930.0, 1464411153.0, 1022607788.0 },
|
samer@0
|
82 { 2824425944.0, 32183930.0, 2093834863.0 }
|
samer@0
|
83 };
|
samer@0
|
84
|
samer@0
|
85
|
samer@0
|
86
|
samer@0
|
87
|
samer@0
|
88
|
samer@0
|
89 /*-------------------------------------------------------------------------*/
|
samer@0
|
90
|
samer@0
|
91 static double MultModM (double a, double s, double c, double m)
|
samer@0
|
92 /* Compute (a*s + c) % m. m must be < 2^35. Works also for s, c < 0 */
|
samer@0
|
93 {
|
samer@0
|
94 double v;
|
samer@0
|
95 long a1;
|
samer@0
|
96 v = a * s + c;
|
samer@0
|
97 if ((v >= two53) || (v <= -two53)) {
|
samer@0
|
98 a1 = (long) (a / two17);
|
samer@0
|
99 a -= a1 * two17;
|
samer@0
|
100 v = a1 * s;
|
samer@0
|
101 a1 = (long) (v / m);
|
samer@0
|
102 v -= a1 * m;
|
samer@0
|
103 v = v * two17 + a * s + c;
|
samer@0
|
104 }
|
samer@0
|
105 a1 = (long) (v / m);
|
samer@0
|
106 if ((v -= a1 * m) < 0.0)
|
samer@0
|
107 return v += m;
|
samer@0
|
108 else
|
samer@0
|
109 return v;
|
samer@0
|
110 }
|
samer@0
|
111
|
samer@0
|
112
|
samer@0
|
113 /*-------------------------------------------------------------------------*/
|
samer@0
|
114
|
samer@0
|
115
|
samer@0
|
116 static void print_vec(double *p)
|
samer@0
|
117 {
|
samer@0
|
118 int i;
|
samer@0
|
119 for (i=0; i<6; i++) printf("%.0lf ",p[i]);
|
samer@0
|
120 printf("\n");
|
samer@0
|
121 }
|
samer@0
|
122
|
samer@0
|
123
|
samer@0
|
124 static void MatVecModM (double A[3][3], double s[3], double v[3], double m)
|
samer@0
|
125 /* Returns v = A*s % m. Assumes that -m < s[i] < m. */
|
samer@0
|
126 /* Works even if v = s. */
|
samer@0
|
127 {
|
samer@0
|
128 int i;
|
samer@0
|
129 double x[3];
|
samer@0
|
130 for (i = 0; i < 3; ++i) {
|
samer@0
|
131 x[i] = MultModM (A[i][0], s[0], 0.0, m);
|
samer@0
|
132 x[i] = MultModM (A[i][1], s[1], x[i], m);
|
samer@0
|
133 x[i] = MultModM (A[i][2], s[2], x[i], m);
|
samer@0
|
134 }
|
samer@0
|
135 memcpy(v,x,3*sizeof(double));
|
samer@0
|
136 }
|
samer@0
|
137
|
samer@0
|
138
|
samer@0
|
139 static void MatMatModM (double A[3][3], double B[3][3], double C[3][3],
|
samer@0
|
140 double m)
|
samer@0
|
141 /* Returns C = A*B % m. Work even if A = C or B = C or A = B = C. */
|
samer@0
|
142 {
|
samer@0
|
143 int i, j;
|
samer@0
|
144 double V[3], W[3][3];
|
samer@0
|
145 for (i = 0; i < 3; ++i) {
|
samer@0
|
146 for (j = 0; j < 3; ++j) V[j] = B[j][i];
|
samer@0
|
147 MatVecModM (A, V, V, m);
|
samer@0
|
148 for (j = 0; j < 3; ++j) W[j][i] = V[j];
|
samer@0
|
149 }
|
samer@0
|
150 memcpy(C,W,9*sizeof(double));
|
samer@0
|
151 }
|
samer@0
|
152
|
samer@0
|
153 static void MatTwoPowModM (double A[3][3], double B[3][3], double m, long e)
|
samer@0
|
154 /* Compute matrix B = (A^(2^e) % m); works even if A = B */
|
samer@0
|
155 {
|
samer@0
|
156 int i, j;
|
samer@0
|
157
|
samer@0
|
158 /* initialize: B = A */
|
samer@0
|
159 if (A != B) {
|
samer@0
|
160 for (i = 0; i < 3; i++) {
|
samer@0
|
161 for (j = 0; j < 3; ++j)
|
samer@0
|
162 B[i][j] = A[i][j];
|
samer@0
|
163 }
|
samer@0
|
164 }
|
samer@0
|
165 /* Compute B = A^{2^e} */
|
samer@0
|
166 for (i = 0; i < e; i++)
|
samer@0
|
167 MatMatModM (B, B, B, m);
|
samer@0
|
168 }
|
samer@0
|
169
|
samer@0
|
170
|
samer@0
|
171 /* returns the next integer in the sequence
|
samer@0
|
172 * new state goes in g1
|
samer@0
|
173 * g1 can be the same as g, resulting in
|
samer@0
|
174 * an update in place.
|
samer@0
|
175 */
|
samer@0
|
176 double next(RngStream g, RngStream g1)
|
samer@0
|
177 {
|
samer@0
|
178 double p1, p2;
|
samer@0
|
179
|
samer@0
|
180 // printf("before: "); print_vec(g->Cg);
|
samer@0
|
181 /* Component 1 */
|
samer@0
|
182 p1 = a12 * g->Cg[1] - a13n * g->Cg[0];
|
samer@0
|
183 p1 -= m1*floor(p1/m1);
|
samer@0
|
184 g1->Cg[0] = g->Cg[1];
|
samer@0
|
185 g1->Cg[1] = g->Cg[2];
|
samer@0
|
186 g1->Cg[2] = p1;
|
samer@0
|
187
|
samer@0
|
188 /* Component 2 */
|
samer@0
|
189 p2 = a21 * g->Cg[5] - a23n * g->Cg[3];
|
samer@0
|
190 p2 -= m2*floor(p2/m2);
|
samer@0
|
191 g1->Cg[3] = g->Cg[4];
|
samer@0
|
192 g1->Cg[4] = g->Cg[5];
|
samer@0
|
193 g1->Cg[5] = p2;
|
samer@0
|
194
|
samer@0
|
195 // printf("after: "); print_vec(g1->Cg);
|
samer@0
|
196 /* Combination */
|
samer@0
|
197 return (p1 > p2) ? (p1 - p2) : (p1 - p2 + m1);
|
samer@0
|
198 }
|
samer@0
|
199
|
samer@0
|
200
|
samer@0
|
201 /*-------------------------------------------------------------------------*/
|
samer@0
|
202 static int CheckSeed (unsigned seed[6])
|
samer@0
|
203 {
|
samer@0
|
204 /* Check that the seeds are legitimate values. Returns 0 if legal seeds,
|
samer@0
|
205 * i:1..6 if seed[i] is bad
|
samer@0
|
206 * 8 if seeds 1..3 are zero
|
samer@0
|
207 * 9 if seeds 4..6 are zero
|
samer@0
|
208 */
|
samer@0
|
209 int i;
|
samer@0
|
210
|
samer@0
|
211 for (i = 0; i < 3; ++i) {
|
samer@0
|
212 if (seed[i] >= m1) return i+1;
|
samer@0
|
213 }
|
samer@0
|
214 for (i = 3; i < 6; ++i) {
|
samer@0
|
215 if (seed[i] >= m2) return i+1;
|
samer@0
|
216 }
|
samer@0
|
217 if (seed[0] == 0 && seed[1] == 0 && seed[2] == 0) return 8;
|
samer@0
|
218 if (seed[3] == 0 && seed[4] == 0 && seed[5] == 0) return 9;
|
samer@0
|
219
|
samer@0
|
220 return 0;
|
samer@0
|
221 }
|
samer@0
|
222
|
samer@0
|
223
|
samer@0
|
224 /*---------------------------------------------------------------------*/
|
samer@0
|
225 /* Public part. */
|
samer@0
|
226 /*---------------------------------------------------------------------*/
|
samer@0
|
227
|
samer@0
|
228
|
samer@0
|
229 void RngStream_InitState(RngStream g) {
|
samer@0
|
230 memcpy(g->Cg,nextSeed,6*sizeof(double));
|
samer@0
|
231 //printf("init: "); print_vec(g->Cg);
|
samer@0
|
232 }
|
samer@0
|
233
|
samer@0
|
234 void RngStream_InitJump(struct rng_jump *j, int e)
|
samer@0
|
235 {
|
samer@0
|
236 void *a1, *a2;
|
samer@0
|
237
|
samer@0
|
238 if (e>=127) { a1=A1p127; a2=A2p127; e-=127; }
|
samer@0
|
239 else if (e==76) { a1=A1p76; a2=A2p76; e-=76; }
|
samer@0
|
240 else { a1=A1p0; a2=A2p0; }
|
samer@0
|
241
|
samer@0
|
242 memcpy(j->T1,a1,9*sizeof(double));
|
samer@0
|
243 memcpy(j->T2,a2,9*sizeof(double));
|
samer@0
|
244 MatTwoPowModM(j->T1,j->T1,m1,e);
|
samer@0
|
245 MatTwoPowModM(j->T2,j->T2,m2,e);
|
samer@0
|
246 }
|
samer@0
|
247
|
samer@0
|
248 void RngStream_DoubleJump(struct rng_jump *j1, struct rng_jump *j2)
|
samer@0
|
249 {
|
samer@0
|
250 MatMatModM(j1->T1,j1->T1,j2->T1,m1);
|
samer@0
|
251 MatMatModM(j1->T2,j1->T2,j2->T2,m2);
|
samer@0
|
252 }
|
samer@0
|
253
|
samer@0
|
254 void RngStream_Advance(struct rng_jump *j, RngStream g1, RngStream g2)
|
samer@0
|
255 {
|
samer@0
|
256 MatVecModM (j->T1, &g1->Cg[0], &g2->Cg[0], m1);
|
samer@0
|
257 MatVecModM (j->T2, &g1->Cg[3], &g2->Cg[3], m2);
|
samer@0
|
258 }
|
samer@0
|
259
|
samer@0
|
260 double RngStream_Float(RngStream g, RngStream g1) {
|
samer@0
|
261 return norm*next(g,g1);
|
samer@0
|
262 }
|
samer@0
|
263
|
samer@0
|
264 double RngStream_Double(RngStream g, RngStream g1) {
|
samer@0
|
265 double u = RngStream_Float(g,g1);
|
samer@0
|
266 u += fact*RngStream_Float(g1,g1);
|
samer@0
|
267 return (u < 1.0) ? u : (u - 1.0);
|
samer@0
|
268 }
|
samer@0
|
269
|
samer@0
|
270 double RngStream_Raw(RngStream g, RngStream g1) {
|
samer@0
|
271 return next(g,g1);
|
samer@0
|
272 }
|
samer@0
|
273
|
samer@0
|
274 /*-------------------------------------------------------------------------*/
|
samer@0
|
275
|
samer@0
|
276 int RngStream_SetState (RngStream g, unsigned seed[6])
|
samer@0
|
277 {
|
samer@0
|
278 int i;
|
samer@0
|
279 if (CheckSeed (seed))
|
samer@0
|
280 return -1; /* FAILURE */
|
samer@0
|
281 for (i = 0; i < 6; ++i)
|
samer@0
|
282 g->Cg[i] = (double)seed[i];
|
samer@0
|
283
|
samer@0
|
284 return 0; /* SUCCESS */
|
samer@0
|
285 }
|
samer@0
|
286
|
samer@0
|
287
|
samer@0
|
288 void RngStream_GetState (RngStream g, unsigned seed[6])
|
samer@0
|
289 {
|
samer@0
|
290 int i;
|
samer@0
|
291 for (i = 0; i < 6; ++i) seed[i] = (unsigned)g->Cg[i];
|
samer@0
|
292 }
|
samer@0
|
293
|