The ROme OpTimistic Simulator  2.0.0
A General-Purpose Multithreaded Parallel/Distributed Simulation Platform
numerical.c
Go to the documentation of this file.
1 
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <math.h>
34 #include <unistd.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 
38 #include <ROOT-Sim.h>
39 
40 #include <core/init.h>
42 #include <mm/mm.h>
43 #include <scheduler/process.h>
44 #include <scheduler/scheduler.h>
45 #include <statistics/statistics.h> // To have _mkdir helper function
46 
53 
54 static double do_random(void)
55 {
56  uint32_t *seed1;
57  uint32_t *seed2;
58 
59  seed1 = (uint32_t *) & (current->numerical.seed);
60  seed2 =
61  (uint32_t *) ((char *)&(current->numerical.seed) +
62  (sizeof(uint32_t)));
63 
64  *seed1 = 36969u * (*seed1 & 0xFFFFu) + (*seed1 >> 16u);
65  *seed2 = 18000u * (*seed2 & 0xFFFFu) + (*seed2 >> 16u);
66 
67  // The magic number below is 1/(2^32 + 2).
68  // The result is strictly between 0 and 1.
69  return (((*seed1 << 16u) + (*seed1 >> 16u) + *seed2) +
70  1.0) * 2.328306435454494e-10;
71 
72 }
73 
80 double Random(void)
81 {
82  double ret;
83  switch_to_platform_mode();
84 
85  ret = do_random();
86 
87  switch_to_application_mode();
88  return ret;
89 }
90 
91 int RandomRange(int min, int max)
92 {
93  double ret;
94  switch_to_platform_mode();
95 
96  ret = (int)floor(do_random() * (max - min + 1)) + min;
97 
98  switch_to_application_mode();
99  return ret;
100 }
101 
102 int RandomRangeNonUniform(int x, int min, int max)
103 {
104  double ret;
105  switch_to_platform_mode();
106 
107  ret = (((RandomRange(0, x) | RandomRange(min, max))) % (max - min + 1)) + min;
108 
109  switch_to_application_mode();
110  return ret;
111 }
112 
120 double Expent(double mean)
121 {
122  double ret;
123  switch_to_platform_mode();
124 
125  if (unlikely(mean < 0)) {
126  rootsim_error(true,
127  "Expent() has been passed a negative mean value\n");
128  }
129 
130  ret = (-mean * log(1 - do_random()));
131 
132  switch_to_application_mode();
133  return ret;
134 }
135 
141 double Normal(void)
142 {
143  double fac, rsq, v1, v2;
144  bool *iset;
145  double *gset;
146 
147  iset = &current->numerical.iset;
148  gset = &current->numerical.gset;
149 
150  if (*iset == false) {
151  do {
152  v1 = 2.0 * Random() - 1.0;
153  v2 = 2.0 * Random() - 1.0;
154  rsq = v1 * v1 + v2 * v2;
155  } while (rsq >= 1.0 || D_EQUAL_ZERO(rsq));
156 
157  fac = sqrt(-2.0 * log(rsq) / rsq);
158 
159  // Perform Box-Muller transformation to get two normal deviates. Return one
160  // and save the other for next time.
161  *gset = v1 * fac;
162  *iset = true;
163  return v2 * fac;
164  } else {
165  // A deviate is already available
166  *iset = false;
167  return *gset;
168  }
169 }
170 
179 double Gamma(int ia)
180 {
181  int j;
182  double am, e, s, v1, v2, x, y;
183 
184  if (unlikely(ia < 1)) {
185  rootsim_error(false,
186  "Gamma distribution must have a ia value >= 1. Defaulting to 1...");
187  ia = 1;
188  }
189 
190  if (ia < 6) {
191  // Use direct method, adding waiting times
192  x = 1.0;
193  for (j = 1; j <= ia; j++)
194  x *= Random();
195  x = -log(x);
196  } else {
197  // Use rejection method
198  do {
199  do {
200  do {
201  v1 = Random();
202  v2 = 2.0 * Random() - 1.0;
203  } while (v1 * v1 + v2 * v2 > 1.0);
204 
205  y = v2 / v1;
206  am = (double)(ia - 1);
207  s = sqrt(2.0 * am + 1.0);
208  x = s * y + am;
209  } while (x < 0.0);
210 
211  e = (1.0 + y * y) * exp(am * log(x / am) - s * y);
212  } while (Random() > e);
213  }
214 
215  return x;
216 }
217 
223 double Poisson(void)
224 {
225  return Gamma(1);
226 }
227 
237 int Zipf(double skew, int limit)
238 {
239  double a = skew;
240  double b = pow(2., a - 1.);
241  double x, t, u, v;
242  do {
243  u = Random();
244  v = Random();
245  x = floor(pow(u, -1. / a - 1.));
246  t = pow(1. + 1. / x, a - 1.);
247  } while (v * x * (t - 1.) / (b - 1.) > (t / b) || x > limit);
248  return (int)x;
249 }
250 
264 {
265 
266  uint32_t *seed1 = (uint32_t *) &(cur_seed);
267  uint32_t *seed2 = (uint32_t *) ((char *)&(cur_seed) + (sizeof(uint32_t)));
268 
269  // Sanitize seed1
270  // Any integer multiple of 0x9068FFFF, including 0, is a bad state
271  uint32_t state_orig;
272  uint32_t temp;
273 
274  state_orig = *seed1;
275  temp = state_orig;
276 
277  // The following is equivalent to % 0x9068FFFF, without using modulo
278  // operation which may be expensive on embedded targets. For
279  // uint32_t and this divisor, we only need 'if' rather than 'while'.
280  if (temp >= UINT32_C(0x9068FFFF))
281  temp -= UINT32_C(0x9068FFFF);
282  if (temp == 0) {
283  // Any integer multiple of 0x9068FFFF, including 0, is a bad state.
284  // Use an alternate state value by inverting the original value.
285  temp = state_orig ^ UINT32_C(0xFFFFFFFF);
286  if (temp >= UINT32_C(0x9068FFFF))
287  temp -= UINT32_C(0x9068FFFF);
288  }
289  *seed1 = temp;
290 
291  // Sanitize seed2
292  // Any integer multiple of 0x464FFFFF, including 0, is a bad state.
293  state_orig = *seed2;
294  temp = state_orig;
295 
296  // The following is equivalent to % 0x464FFFFF, without using modulo
297  // operation which may be expensive on some targets. For
298  // uint32_t and this divisor, it may loop up to 3 times.
299  while (temp >= UINT32_C(0x464FFFFF))
300  temp -= UINT32_C(0x464FFFFF);
301  if (temp == 0) {
302  // Any integer multiple of 0x464FFFFF, including 0, is a bad state.
303  // Use an alternate state value by inverting the original value.
304  temp = state_orig ^ UINT32_C(0xFFFFFFFF);
305  while (temp >= UINT32_C(0x464FFFFF))
306  temp -= UINT32_C(0x464FFFFF);
307  }
308 
309  *seed2 = temp;
310 
311  return cur_seed;
312 }
313 
326 static void load_seed(void)
327 {
328 
329  static bool single_print = false; // To print only once a message about manual initialization
330  seed_type new_seed;
331  char conf_file[512];
332  FILE *fp;
333 
334  // Get the path to the configuration file
335  sprintf(conf_file, "%s/.rootsim/numerical.conf", getenv("HOME"));
336 
337  // Check if the file exists. If not, we have to create configuration
338  if ((fp = fopen(conf_file, "r+")) == NULL) {
339 
340  // Try to build the path to the configuration folder.
341  sprintf(conf_file, "%s/.rootsim", getenv("HOME"));
342  _mkdir(conf_file);
343 
344  // Create and initialize the file
345  sprintf(conf_file, "%s/.rootsim/numerical.conf", getenv("HOME"));
346  if ((fp = fopen(conf_file, "w")) == NULL) {
347  rootsim_error(true, "Unable to create the numerical library configuration file %s. Aborting...", conf_file);
348  }
349 
350  // We now initialize the first long random number. Thanks Unix!
351  // TODO: THIS IS NOT PORTABLE!
352  int fd;
353  if ((fd = open("/dev/random", O_RDONLY)) == -1) {
354  rootsim_error(true, "Unable to initialize the numerical library configuration file %s. Aborting...", conf_file);
355 
356  }
357  read(fd, &new_seed, sizeof(seed_type));
358  close(fd);
359  fprintf(fp, "%llu\n", (unsigned long long)new_seed); // We cast, so that we get an integer representing just a bit sequence
360  fclose(fp);
361 
362  }
363 
364  // Is seed manually specified?
365  if (rootsim_config.set_seed > 0) {
366 
367  if (!single_print) {
368  single_print = true;
369  printf("Manually setting master seed to %llu\n", (unsigned long long)rootsim_config.set_seed);
370  }
372  }
373 
374  // Load the configuration for the numerical library
375  if ((fp = fopen(conf_file, "r+")) == NULL) {
376  rootsim_error(true, "Unable to load numerical distribution configuration: %s. Aborting...", conf_file);
377  }
378 
379  // Load the initial seed
380  fscanf(fp, "%llu", (unsigned long long *)&master_seed);
381 
382  // Replace the initial seed
383  if (rootsim_config.deterministic_seed == false) {
384  rewind(fp);
385  srandom(master_seed);
386  new_seed = random();
387  fprintf(fp, "%llu\n", (unsigned long long)new_seed);
388  }
389 
390  fclose(fp);
391 }
392 
393 
394 // TODO: con un (non tanto) alto numero di processi logici (> numero di bit di un intero!!), lo shift
395 // circolare restituisce a due LP differenti lo stesso seme iniziale. Questo fa sì che, se la logica di
396 // programma è la stessa e basata soltanto su numeri casuali, due o più nodi eseguano sempre la stessa
397 // sequenza di eventi agli stessi timestamp!!!
398 
399 #define RS_WORD_LENGTH (8 * sizeof(seed_type))
400 #define ROR(value, places) (value << (places)) | (value >> (RS_WORD_LENGTH - places)) // Circular shift
401 void numerical_init(void)
402 {
403  // Initialize the master seed
404  load_seed();
405 
406  // Initialize the per-LP seed
407  foreach_lp(lp) {
408  lp->numerical.seed = sanitize_seed(ROR((int64_t) master_seed, lp->gid.to_int % RS_WORD_LENGTH));
409  }
410 
411 }
412 
413 #undef RS_WORD_LENGTH
414 #undef ROR
415 
416 
431 __attribute__((const))
432 double NeumaierSum(unsigned cnt, double addendums[cnt]) {
433  if(!cnt)
434  return 0.0;
435  double sum = addendums[0];
436  double crt = 0.0;
437  double tmp;
438  while (--cnt) {
439  tmp = sum + addendums[cnt];
440  if(fabs(sum) >= fabs(addendums[cnt])) {
441  crt += (sum - tmp) + addendums[cnt];
442  } else {
443  crt += (addendums[cnt] - tmp) + sum;
444  }
445  sum = tmp;
446  }
447  return sum + crt;
448 }
449 
450 
462 __attribute__((const))
463 struct _sum_helper_t PartialNeumaierSum(struct _sum_helper_t sh, double addendum){
464  double tmp = sh.sum + addendum;
465  if(fabs(sh.sum) >= fabs(addendum)) {
466  sh.crt += (sh.sum - tmp) + addendum;
467  } else {
468  sh.crt += (addendum - tmp) + sh.sum;
469  }
470  sh.sum = tmp;
471  return sh;
472 }
473 
Communication Routines.
uint64_t seed_type
Numerical seed type.
Definition: numerical.h:36
Initialization routines.
#define min(a, b)
Macro to find the minimum among two values.
Definition: core.h:115
static void load_seed(void)
Definition: numerical.c:326
seed_type set_seed
The master seed to be used in this run.
Definition: init.h:73
double Normal(void)
Definition: numerical.c:141
ROOT-Sim header for model development.
static seed_type sanitize_seed(seed_type cur_seed)
Definition: numerical.c:263
void _mkdir(const char *path)
Definition: statistics.c:198
struct _sum_helper_t PartialNeumaierSum(struct _sum_helper_t sh, double addendum)
Definition: numerical.c:463
double Gamma(int ia)
Definition: numerical.c:179
bool deterministic_seed
Does not change the seed value config file that will be read during the next runs.
Definition: init.h:69
Statistics module.
The ROOT-Sim scheduler main module header.
seed_type seed
Definition: numerical.h:43
__thread struct lp_struct * current
This is a per-thread variable pointing to the block state of the LP currently scheduled.
Definition: scheduler.c:72
this represents a partial Neumaier sum
Definition: numerical.h:55
simulation_configuration rootsim_config
This global variable holds the configuration for the current simulation.
Definition: core.c:70
Memory Manager main header.
LP control blocks.
#define max(a, b)
Macro to find the maximum among two values.
Definition: core.h:106
#define D_EQUAL_ZERO(a)
Equality to zero condition for doubles.
Definition: core.h:96
int Zipf(double skew, int limit)
Definition: numerical.c:237
double NeumaierSum(unsigned cnt, double addendums[cnt])
Definition: numerical.c:432
double Poisson(void)
Definition: numerical.c:223
static seed_type master_seed
Definition: numerical.c:52
double Random(void)
Definition: numerical.c:80
double Expent(double mean)
Definition: numerical.c:120
#define unlikely(exp)
Optimize the branch as likely not taken.
Definition: core.h:74