core/random.c

Go to the documentation of this file.
00001 
00011 #include <stdlib.h>
00012 #include <math.h>
00013 #include <errno.h>
00014 #include <string.h>
00015 #include <stdarg.h>
00016 #include <time.h>
00017 #include <float.h>
00018 #include "random.h"
00019 #include "find.h"
00020 #include "output.h"
00021 #include "exception.h"
00022 
00023 #ifdef WIN32
00024 #define finite _finite
00025 #endif
00026 
00027 #define QNAN sqrt(-1) /* quiet NaN used only for low-key failures */
00028 
00029 int random_init(void)
00030 {
00031     /* randomizes the random number generator start value */
00032     srand((unsigned int)time(NULL));
00033     return 1;
00034 }
00035 
00038 RANDOMTYPE random_type(char *name) 
00039 {
00040     struct {
00041         char *name;
00042         RANDOMTYPE type;
00043     } *p, map[] = {
00044         {"degenerate", RT_DEGENERATE},
00045         {"uniform",RT_UNIFORM},
00046         {"normal",RT_NORMAL},
00047         {"bernoulli",RT_BERNOULLI},
00048         {"sampled",RT_SAMPLED},
00049         {"pareto",RT_PARETO},
00050         {"lognormal",RT_LOGNORMAL},
00051         {"exponential",RT_EXPONENTIAL},
00053     };
00054     for (p=map; p<map+sizeof(map)/sizeof(map[0]); p++)
00055     {
00056         if (strcmp(p->name,name)==0)
00057             return p->type;
00058     }
00059     return RT_INVALID;
00060 }
00061 
00062 /* uniform distribution in range (0,1( */
00063 double randunit(void)
00064 {
00065     double u = rand()/(RAND_MAX+1.0);
00066     if (u<1) return u;
00067     return randunit();
00068 }
00069 
00076 double random_degenerate(double a)
00077 {
00078     /* returns a, i.e., Dirac delta function */
00079     double aa = fabs(a);
00080     if (a!=0 && (aa<1e-30 || aa>1e30))
00081         output_warning("random_degenerate(a=%g): a is outside normal bounds of +/-1e(+/-30)", a);
00082     return a;
00083 }
00084 
00098 double random_uniform(double a, 
00099                       double b) 
00100 {
00101     /* uniform distribution in range (a,b( */
00102     double aa=fabs(a), ab=fabs(b);
00103     if (a!=0 && (aa<1e-30 || aa>1e30))
00104         output_warning("random_uniform(a=%g, b=%g): a is outside normal bounds of +/-1e(+/-30)", a, b);
00105     if (b!=0 && (ab<1e-30 || ab>1e30))
00106         output_warning("random_uniform(a=%g, b=%g): b is outside normal bounds of +/-1e(+/-30)", a, b);
00107     if (b<a)
00108         output_warning("random_uniform(a=%g, b=%g): b is less than a", a, b);
00109     return randunit()*(b-a)+a;
00110 }
00111 
00133 double random_normal(double m, 
00134                      double s) 
00135 {
00136     /* normal distribution centered on m, with variance s^2 */
00137     double r=randunit();
00138     if (s<0)
00139         output_warning("random_normal(m=%g, s=%g): s is negative", m, s);
00140     while (r<=0 || r>1)
00141         r=randunit();
00142     return sqrt(-2*log(r)) * sin(2*PI*randunit())*s+m;
00143 }
00144 
00152 double random_bernoulli(double p) 
00153 {
00154     double ap = fabs(p);
00155     if (ap!=0 && (ap<1e-30 || ap>1e30))
00156         output_warning("random_bernoulli(p=%g): p is not within the normal bounds of +/-1e(+/-30)", p);
00157     if (p<0 || p>1)
00158         output_warning("random_bernoulli(p=%g): p is not between 0 and 1", p);
00159     /* 1 if rand<=p, 0 if rand>p */
00160     return (p>=randunit()) ? 1 : 0;
00161 }
00162 
00171 double random_sampled(unsigned n, 
00172                       double *x) 
00173 {
00174     if (n>0)
00175     {
00176         double v = x[(unsigned)(randunit()*n)];
00177         double av = fabs(v);
00178         if (v!=0 && (v<1e-30 || v>1e30))
00179             output_warning("random_sampled(n=%d,...): sampled value is not within normal bounds of +/-1e(+/-30)",n);
00180         return v;
00181     }
00182     else
00183     {
00184         throw_exception("random_sampled(n=%d,...): n must be a positive number", n);
00185         return QNAN; /* never gets here */
00186     }
00187 }
00188 
00189 
00198 double random_pareto(double m, 
00199                      double k) 
00200 {
00201     double am = fabs(m);
00202     double ak = fabs(k);
00203     double r = randunit();
00204     if (am!=0 && (am<1e-03 || am>1e30))
00205         output_warning("random_pareto(m=%g, k=%g): m is not within the normal bounds of +/-1e(+/-30)", m, k);
00206     if (ak>1e30)
00207         output_warning("random_pareto(m=%g, k=%g): k is not within the normal bounds of +/-1e(+/-30)", m, k);
00208     if (k<=0)
00209         throw_exception("random_pareto(m=%g, k=%g): k must be greater than 1", m, k);
00210     while (r<=0 || r>=1)
00211         r = randunit();
00212     return m*pow(r,-1/k);
00213 }
00214 
00220 double random_lognormal(double gmu, 
00221                         double gsigma) 
00222 {
00223     return exp(random_normal(0,1)*gsigma+gmu);
00224 }
00225 
00237 double random_exponential(double lambda) 
00238 {
00239     double r=randunit();
00240     if (lambda<=0)
00241         throw_exception("random_exponential(l=%g): l must be greater than 0", lambda);
00242     if (lambda<1e-30 || lambda>1e30)
00243         output_warning("random_exponential(l=%g): l is not within the normal bounds of 1e(+/-30)", lambda);
00244     while (r<=0 || r>=1)
00245         r=randunit();
00246     return -log(r)/lambda;
00247 }
00248 
00249 /* internal function that generates a random number */
00250 static double _random_value(RANDOMTYPE type, va_list ptr)
00251 {
00252     switch (type) {
00253     case RT_DEGENERATE:/* ... double value */
00254         return random_degenerate(va_arg(ptr,double));
00255     case RT_UNIFORM:        /* ... double min, double max */
00256         {   double min = va_arg(ptr,double);
00257             double max = va_arg(ptr,double);
00258             return random_normal(min,max);
00259         }
00260     case RT_NORMAL:     /* ... double mean, double stdev */
00261         {   double mu = va_arg(ptr,double);
00262             double sigma = va_arg(ptr,double);
00263             return random_normal(mu,sigma);
00264         }
00265     case RT_BERNOULLI:  /* ... double p */
00266         return random_bernoulli(va_arg(ptr,double));
00267     case RT_SAMPLED: /* ... unsigned n_samples, double samples[n_samples] */
00268         {   unsigned n_samples = va_arg(ptr,unsigned);
00269             double *samples = va_arg(ptr,double*);
00270             return random_sampled(n_samples,samples);
00271         }
00272     case RT_PARETO: /* ... double base, double gamma */
00273         {   double base = va_arg(ptr,double);
00274             double gamma = va_arg(ptr,double);
00275             return random_pareto(base,gamma);
00276         }
00277     case RT_LOGNORMAL:  /* ... double gmean, double gsigma */
00278         {   double gmu = va_arg(ptr,double);
00279             double gsigma = va_arg(ptr,double);
00280             return random_lognormal(gmu,gsigma);
00281         }
00282     case RT_EXPONENTIAL: /* ... */
00283         {   double lambda = va_arg(ptr,double);
00284             return random_exponential(lambda);
00285         }
00286     default:
00287         throw_exception("_random_value(type=%d,...); type is not valid",type);
00288     }
00289     return QNAN; /* never gets here */
00290 }
00291 
00295 int random_apply(char *group_expression, 
00296                  char *property, 
00297                  RANDOMTYPE type, 
00298                  ...) 
00299 {
00300     FINDLIST *list = find_objects(FL_GROUP, group_expression);
00301     OBJECT *obj;
00302     unsigned count=0;
00303     va_list ptr;
00304     va_start(ptr,type);
00305     for (obj=find_first(list); obj!=NULL; find_next(list,obj))
00306     {
00307         /* this is quite slow and should use a class property lookup */
00308         object_set_double_by_name(obj,property,_random_value(type,ptr));
00309         count++;
00310     }
00311     va_end(ptr);
00312     return count;
00313 }
00314 
00318 double random_value(RANDOMTYPE type, 
00319                     ...) 
00320 {
00321     double x;
00322     va_list ptr;
00323     va_start(ptr,type);
00324     x = _random_value(type,ptr);
00325     va_end(ptr);
00326     return x;
00327 }
00328 
00329 /******************************************************************************/
00330 static double mean(double sample[], unsigned int count)
00331 {
00332     double sum=0;
00333     unsigned int i;
00334     for (i=0; i<count; i++)
00335         sum += sample[i];
00336     return sum/count;
00337 }
00338 #ifdef min
00339 #undef min
00340 #endif
00341 static double min(double sample[], unsigned int count)
00342 {
00343     double min;
00344     unsigned int i;
00345     for (i=0; i<count; i++)
00346     {
00347         if (i==0) min=sample[0];
00348         else if (sample[i]<min) min=sample[i];
00349     }
00350     return min;
00351 }
00352 #ifdef max
00353 #undef max
00354 #endif
00355 static double max(double sample[], unsigned int count)
00356 {
00357     double max;
00358     unsigned int i;
00359     for (i=0; i<count; i++)
00360     {
00361         if (i==0) max=sample[0];
00362         else if (sample[i]>max) max=sample[i];
00363     }
00364     return max;
00365 }
00366 static double stdev(double sample[], unsigned int count)
00367 {
00368     double sum=0, mean=0;
00369     unsigned int n = 0, i;
00370     for (i=0; i<count; i++)
00371     {
00372         double delta = sample[i] - mean;
00373         n++;
00374         mean += delta/n;
00375         sum += delta*(sample[i]-mean);
00376     }
00377     return sqrt(sum/(n-1));
00378 }
00379 static void sort(double sample[], unsigned int count)
00380 {
00381 }
00382 static void report(char *parameter, double actual, double expected)
00383 {
00384     if (parameter==NULL)
00385     {
00386         output_test("   Parameter       Actual    Expected    Error");
00387         output_test("---------------- ---------- ---------- ----------");
00388     }
00389     else
00390     {
00391         if (expected==0)
00392             output_test("%-16.16s %10.4f %10.4f %9.6f ", parameter,actual,expected,actual-expected);
00393         else
00394             output_test("%-16.16s %10.4f %10.4f %9.6f%%", parameter,actual,expected,(actual-expected)/expected*100);
00395     }
00396 }
00397 
00404 int random_test(void)
00405 {
00406     static double sample[1000000];
00407     double a, b;
00408     unsigned int count = sizeof(sample)/sizeof(sample[0]);
00409     unsigned int i;
00410 
00411     /* Dirac distribution test */
00412     a = 10*randunit()/2-5;
00413     output_test("\ndegenerate(x=%g)",a);
00414     for (i=0; i<count; i++)
00415     {
00416         sample[i] = random_degenerate(a);
00417         if (!finite(sample[i]))
00418             output_test("Sample %d is not a finite number!",i--);
00419     }
00420     report(NULL,0,0);
00421     report("Mean",mean(sample,count),a);
00422     report("Stdev",stdev(sample,count),0);
00423     report("Min",min(sample,count),a);
00424     report("Max",max(sample,count),a);
00425 
00426     /* uniform distribution test */
00427     a = 10*randunit()/2;
00428     b = 10*randunit()/2 + 5;
00429     output_test("\nuniform(min=%g, max=%g)",a,b);
00430     for (i=0; i<count; i++)
00431     {
00432         sample[i] = random_uniform(a,b);
00433         if (!finite(sample[i]))
00434             output_test("Sample %d is not a finite number!",i--);
00435     }
00436     report(NULL,0,0);
00437     report("Mean",mean(sample,count),(a+b)/2);
00438     report("Stdev",stdev(sample,count),sqrt((b-a)*(b-a)/12));
00439     report("Min",min(sample,count),a);
00440     report("Max",max(sample,count),b);
00441 
00442     /* Bernoulli distribution test */
00443     a = randunit();
00444     output_test("\nBernoulli(prob=%g)",a);
00445     for (i=0; i<count; i++)
00446     {
00447         sample[i] = random_bernoulli(a);
00448         if (!finite(sample[i]))
00449             output_test("Sample %d is not a finite number!",i--);
00450     }
00451     report(NULL,0,0);
00452     report("Mean",mean(sample,count),a);
00453     report("Stdev",stdev(sample,count),sqrt(a*(1-a)));
00454     report("Min",min(sample,count),0);
00455     report("Max",max(sample,count),1);
00456 
00457     /* normal distribution test */
00458     a = 20*randunit()-5;
00459     b = 5*randunit();
00460     output_test("\nnormal(mean=%g, stdev=%g)",a,b);
00461     for (i=0; i<count; i++)
00462     {
00463         sample[i] = random_normal(a,b);
00464         if (!finite(sample[i]))
00465             output_test("Sample %d is not a finite number!",i--);
00466     }
00467     report(NULL,0,0);
00468     report("Mean",mean(sample,count),a);
00469     report("Stdev",stdev(sample,count),b);
00470     
00471     /* exponential distribution test */
00472     a = 1/randunit()-1;
00473     output_test("\nexponential(lambda=%g)",a);
00474     for (i=0; i<count; i++)
00475     {
00476         sample[i] = random_exponential(a);
00477         if (!finite(sample[i]))
00478             output_test("Sample %d is not a finite number!",i--);
00479     }
00480     report(NULL,0,0);
00481     report("Mean",mean(sample,count),1/a);
00482     report("Stdev",stdev(sample,count),1/a);
00483     report("Min",min(sample,count),0);
00484     
00485     /* lognormal distribution test */
00486     a = 2*randunit()-1;
00487     b = 2*randunit();
00488     output_test("\nlognormal(gmean=%g, gstdev=%g)",a,b);
00489     for (i=0; i<count; i++)
00490     {
00491         sample[i] = random_lognormal(a,b);
00492         if (!finite(sample[i]))
00493             output_test("Sample %d is not a finite number!",i--);
00494     }
00495     report(NULL,0,0);
00496     report("Mean",mean(sample,count),exp(a+b*b/2));
00497     report("Stdev",stdev(sample,count),sqrt((exp(b*b)-1)*exp(2*a+b*b)));
00498     report("Min",min(sample,count),0);
00499 
00500     /* Pareto distribution test */
00501     a = 10*randunit();
00502     b = randunit()*2+2;
00503     output_test("\nPareto(base=%g, gamma=%g)",a,b);
00504     for (i=0; i<count; i++)
00505     {
00506         sample[i] = random_pareto(a,b);
00507         if (!finite(sample[i]))
00508             output_test("Sample %d is not a finite number!",i--);
00509     }
00510     report(NULL,0,0);
00511     report("Mean",mean(sample,count),b*a/(b-1));
00512     report("Stdev",stdev(sample,count),sqrt(a*a*b/((b-1)*(b-1)*(b-2))));
00513     report("Min",min(sample,count),a);
00514 
00515     /* sampled distribution test */
00516     output_test("\nSampled(count=10, sample=[0..9])");
00517     for (i=0; i<count; i++)
00518     {
00519         double set[10]={0,1,2,3,4,5,6,7,8,9};
00520         sample[i] = random_sampled(10,set);
00521         if (!finite(sample[i]))
00522             output_test("Sample %d is not a finite number!",i--);
00523     }
00524     report(NULL,0,0);
00525     report("Mean",mean(sample,count),4.5);
00526     //report("Stdev",stdev(sample,count),sqrt(9*9/12));
00527     report("Stdev",stdev(sample,count),sqrt(99/12)); /* sqrt((b-a+1)^2-1 / 12)*/ /* 2.87 is more accurate and was Mathematica's answer */
00528     report("Min",min(sample,count),0);
00529     report("Max",max(sample,count),9);
00530 
00531     output_warning("random_test() not implemented yet");
00532     return 0;
00533 }
00534 

GridLAB-DTM Version 1.0
An open-source project initiated by the US Department of Energy