Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Ticket #1155: Random.d

File Random.d, 80.3 kB (added by fawzi, 5 months ago)

Complete rewrite of Random with many enhancements, added back .shared static method

Line 
1 /*******************************************************************************
2     Random number generators
3     
4     This is an attempt at having a good flexible and easy to use random number
5     generator.
6     ease of use:
7     - shared generator for quick usage available through the "rand" object
8       int i=rand.uniformR(10); // a random number from [0;10)
9     - simple Random (non threadsafe) and RandomSync (threadsafe) types to
10       create new generators (for heavy use a good idea is one Random object per thread)
11     - several distributions can be requested like this
12         rand.distributionD!(type)(paramForDistribution)
13       the type can often be avoided if the parameters make it clear.
14       From it single numbers can be generated with .getRandom(), and variables
15       initialized either with call style (var) or with .randomize(var).
16       Utility functions to generate numbers directly are also available.
17       The choice to put all the distribution in a single object that caches them
18       has made (for example) the gamma distribution very easy to implement.
19     - sample usage:
20       auto r=new Random();
21       int i; float f; real r; real[100] ar0; real[] ar=ar0[];
22       // initialize with uniform distribution
23       r(i)(f)(r)(ar);
24       // unfortunetely one cannot use directly ar0...
25       // unifrom distribution 0..10
26       r.uniformR(10)(i)(f)(r)(ar);
27       // uniform distribution -10..10
28       r.uniformRSymm(10)(i)(f)(r)(ar);
29       // the distribution can be stored
30       auto r2=r.uniformRSymm(10);
31       // and used later
32       r2(ar);
33       // complex distributions (normal,exp,gamma) are produced for the requested type
34       r.normalSource!(float)()(f);
35       // with sigma=2
36       r.normalD(2.0f)(f);
37       // and can be used also to initialize other types
38       r.normalSource!(float)()(r)(ar);
39       r.normalD(2.0f)(r)(ar);
40       // but this is different from
41       r.normalSource!(real)()(i)(r)(ar);
42       r.normalD(2.0L)(i)(r)(ar);
43       // as the source generates numbers of its type that then are simply casted to
44       // the type needed.
45       // Uniform distribution (as its creation for different types has no overhead)
46       // never cast, so that (for example) bounds exclusion for floats is really
47       // guaranteed.
48       // For the other distribution using a distribution of different type than
49       // the variable should be done with care, as underflow/overflow might ensue.
50       //
51       // some utility functions are also available
52       int i2=r.uniform!(int)();
53       int i2=r.randomize(i); // both i and i2 are initialized to the same value
54       float f2=r.normalSigma(3.0f);
55     flexibility:
56     - easily swappable basic source
57       // a random generator that uses the system provided random generator:
58       auto r=RandomG!(Urandom)();
59     - ziggurat generator can be easily adapted to any decreasing derivable
60       distribution, the hard parametrization (to find xLast) can be done
61       automatically
62     - several distributions available "out of the box"
63     quality:
64     - the default Source (KISS99) passes all statistical tests
65       (P. L'Ecuyer and R. Simard, ACM Transactions on Mathematical Software (2007),
66       33, 4, Article 22)
67     - floating point uniform generator always initializes the full mantissa, the
68       only flaw is a (*very* small) predilection of 0 as least important bit
69       (IEEE rounds to 0 in case of tie).
70       Using a method that initializes the full mantissa was shown to improve the
71       quality of subsequntly derived normal distribued numbers
72       (Thomas et al. Gaussian random number generators. Acm Comput Surv (2007)
73       vol. 39 (4) pp. 11))
74     - Ziggurat method, a very fast and accurate method was used for both Normal and
75       exp distribued numbers.
76     - gamma distribued numbers uses a method recently proposed by Marsaglia and
77       Tsang. The method is very fast, and should be good.
78       My (Fawzi's) feeling is that the transformation h(x)=(1+d*x)^3 might loose
79       a couple of bits of precision in some cases, but it is unclear if this
80       might become visible in (*very* extensive) tests or not.
81     - the basic source can be easily be changed with something else
82     efficiency:
83     - very fast methods have been used, and some effort has been put into
84       optimizing some of them, but not all, but the interface has been choosen
85       so that close to optimal implementation can be provided through the same
86       interface.
87     - Normal and Exp sources allocated only upon request: no memory waste, but
88       a (*very* small) speed hit, that can be avoided by storing the source in
89       a variable and using it (not going through the RandomG)
90     annoyances:
91     - I have put quite some functionality in a single module, maybe some might
92       prefer to split it up
93     - I have added two "next" methods to RandomG for backward compatibility
94       reasons, and replaced the .hared instance from Random has been
95       replaced by the "rand" object. The idea behind this is that RandomG is
96       a template and rand it should be shared across all templates.
97       If the name rand is considered bad one could change it.
98       I kept .shared static method that returns rand, so this remain a dropin
99       replacement of the old random.
100     - you cannot initialize a static array directly, this because randomize is
101       declared like this:
102         U randomize(U)(ref U a) { }
103       and a static array cannot be passed by reference. Removing the ref would
104       make arrays initialized, and scalar not, which is much worse.
105
106         copyright:      Copyright (c) 2008. Fawzi Mohamed
107         license:        BSD style: $(LICENSE)
108         version:        Initial release: July 2008
109         author:         Fawzi Mohamed
110
111 *******************************************************************************/
112 module tango.math.Random;
113 import tango.io.protocol.model.IWriter:IWritable,IWriter;
114 import tango.io.protocol.model.IReader:IReadable,IReader;
115 import Integer = tango.text.convert.Integer;
116 import tango.math.Bracket:findRoot;
117 import tango.core.sync.Mutex: Mutex;
118 import tango.io.FileConduit; // use stdc read/write?
119 import tango.math.Math:exp,sqrt,log,PI,abs;
120 //import tango.math.Probability: normalDistributionCompl;
121 import tango.math.ErrorFunction:erfc;
122 //import tango.stdc.stdio:printf;
123
124 version (Win32)
125          private extern(Windows) int QueryPerformanceCounter (ulong *);
126
127 version (Posix)
128         {
129         private import tango.stdc.posix.sys.time;
130         }
131
132 version(darwin) { version=has_urandom; }
133 version(linux)  { version=has_urandom; }
134
135 /// compile time integer power
136 private T powI(T)(T x,int p){
137     T xx=cast(T)1;
138     if (p<0){
139         p=-p;
140         x=1/x;
141     }
142     for (int i=0;i<p;++i)
143         xx*=x;
144     return xx;
145 }
146
147 /// if T is a float
148 template isFloat(T){
149     static if(is(T==float)||is(T==double)||is(T==real)){
150         const bool isFloat=true;
151     } else {
152         const bool isFloat=false;
153     }
154 }
155
156 /// Strips the []'s off of a type.
157 template arrayBaseT(T)
158 {
159     static if( is( T S : S[]) ) {
160         alias arrayBaseT!(S)  arrayBaseT;
161     }
162     else {
163         alias T arrayBaseT;
164     }
165 }
166
167
168 /// very simple array based source (use with care, some methods in non uniform distributions
169 /// expect a random source with correct statistics, and could loop forever with such a source)
170 struct ArraySource{
171     uint[] a;
172     size_t i;
173     static ArraySource opCall(uint[] a,size_t i=0)
174     in { assert(a.length>0,"array needs at least one element"); }
175     body {
176         ArraySource res;
177         res.a=a;
178         res.i=i;
179         return res;
180     }
181     uint next(){
182         assert(a.length>i,"error, array out of bounds");
183         uint el=a[i];
184         i=(i+1)%a.length;
185         return el;
186     }
187     ubyte nextB(){
188         return cast(ubyte)(0xFF&next);
189     }
190     ulong nextL(){
191         return ((cast(ulong)next)<<32)+cast(ulong)next;
192     }
193 }
194
195 version(has_urandom) {
196     /// basic source that takes data from system random device
197     /// This is an engine, do not use directly, use RandomG!(Urandom)
198     /// should use stdc rad/write?
199     struct URandom{
200         static FileConduit.Style readStyle;
201         static Mutex lock;
202         static this(){
203             readStyle.access=FileConduit.Access.Read;
204             readStyle.open  =FileConduit.Open.Exists;
205             readStyle.share =FileConduit.Share.Read;
206             readStyle.cache =FileConduit.Cache.None;
207
208             lock=new Mutex();
209         }
210         const int canCheckpoint=false;
211         const int canSeed=false;
212    
213         void skip(uint n){ }
214         ubyte nextB(){
215             union ToVoidA{
216                 ubyte i;
217                 void[1] a;
218             }
219             ToVoidA el;
220             synchronized(lock){
221                 auto fn = new FileConduit("/dev/urandom", readStyle);
222                 if(fn.read(el.a)!=el.a.length){
223                     throw new Exception("could not write the requested bytes from urandom");
224                 }
225                 fn.close();
226             }
227             return el.i;
228         }
229         uint next(){
230             union ToVoidA{
231                 uint i;
232                 void[4] a;
233             }
234             ToVoidA el;
235             synchronized(lock){
236                 auto fn = new FileConduit("/dev/urandom", readStyle);
237                 if(fn.read(el.a)!=el.a.length){
238                     throw new Exception("could not write the requested bytes from urandom");
239                 }
240                 fn.close();
241             }
242             return el.i;
243         }
244         ulong nextL(){
245             union ToVoidA{
246                 ulong l;
247                 void[8] a;
248             }
249             ToVoidA el;
250             synchronized(lock){
251                 auto fn = new FileConduit("/dev/urandom", readStyle);
252                 if(fn.read(el.a)!=el.a.length){
253                     throw new Exception("could not write the requested bytes from urandom");
254                 }
255                 fn.close();
256             }
257             return el.l;
258         }
259
260         void seed(uint delegate() r) { }
261         ///  IWritable implementation
262         void write (IWriter input){
263             char[] r="URandom";
264             input(r);
265         }
266         /// IReadable implementation
267         void read (IReader input){
268             char[] r;
269             input(r);
270             assert(r=="URandom","unexpected read");
271         }
272         /// writes the current status in a string
273         char[] toString(){
274             return "URandom";
275         }
276         /// reads the current status from a string (that should have been trimmed)
277         /// returns the number of chars read
278         uint fromString(char[] s){
279             char[] r="URandom";
280             assert(s[0.. r.length]==r,"unxepected string instad of URandom:"~s);
281             return r.length;
282         }
283     }
284 }
285
286 /+ Kiss99 random number generator, by Marisaglia
287 + a simple RNG that passes all statistical tests
288 + This is the engine, *never* use it directly, always use it though a RandomG class
289 +/
290 struct Kiss99{
291     private uint kiss_x = 123456789;
292     private uint kiss_y = 362436000;
293     private uint kiss_z = 521288629;
294     private uint kiss_c = 7654321;
295     private uint nBytes = 0;
296     private uint restB  = 0;
297    
298     const int canCheckpoint=true;
299     const int canSeed=true;
300    
301     void skip(uint n){
302         for (int i=n;i!=n;--i){
303             next;
304         }
305     }
306     ubyte nextB(){
307         if (nBytes>0) {
308             ubyte res=cast(ubyte)(restB & 0xFF);
309             restB >>= 8;
310             --nBytes;
311             return res;
312         } else {
313             restB=next;
314             ubyte res=cast(ubyte)(restB & 0xFF);
315             restB >>= 8;
316             nBytes=3;
317             return res;
318         }
319     }
320     uint next(){
321         const ulong a = 698769069UL;
322         ulong t;
323         kiss_x = 69069*kiss_x+12345;
324         kiss_y ^= (kiss_y<<13); kiss_y ^= (kiss_y>>17); kiss_y ^= (kiss_y<<5);
325         t = a*kiss_z+kiss_c; kiss_c = (t>>32);
326         kiss_z=cast(uint)t;
327         return kiss_x+kiss_y+kiss_z;
328     }
329     ulong nextL(){
330         return ((cast(ulong)next)<<32)+cast(ulong)next;
331     }
332    
333     void seed(uint delegate() r){
334         kiss_x = r();
335         for (int i=0;i<100;++i){
336             kiss_y=r();
337             if (kiss_y!=0) break;
338         }
339         if (kiss_y==0) kiss_y=362436000;
340         kiss_z=r();
341         /* Don’t really need to seed c as well (is reset after a next),
342            but doing it allows to completely restore a given internal state */
343         kiss_c = r() % 698769069; /* Should be less than 698769069 */
344         nBytes = 0;
345         restB=0;
346     }
347     ///  IWritable implementation
348     void write (IWriter input){
349         input(kiss_x)(kiss_y)(kiss_z)(kiss_c)(nBytes)(restB);
350     }
351     /// IReadable implementation
352     void read (IReader input){
353         input(kiss_x)(kiss_y)(kiss_z)(kiss_c)(nBytes)(restB);
354     }
355     /// writes the current status in a string
356     char[] toString(){
357         char[] res=new char[6+6*9];
358         int i=0;
359         res[i..i+6]="KISS99";
360         i+=6;
361         foreach (val;[kiss_x,kiss_y,kiss_z,kiss_c,nBytes,restB]){
362             res[i]='_';
363             ++i;
364             Integer.format(res[i..i+8],val,"x8");
365             i+=8;
366         }
367         assert(i==res.length,"unexpected size");
368         return res;
369     }
370     /// reads the current status from a string (that should have been trimmed)
371     /// returns the number of chars read
372     uint fromString(char[] s){
373         uint i=0;
374         assert(s[i..i+4]=="KISS","unexpected kind, expected KISS");
375         assert(s[i+4..i+7]=="99_","unexpected version, expected 99");
376         i+=6;
377         foreach (val;[&kiss_x,&kiss_y,&kiss_z,&kiss_c,&nBytes,&restB]){
378             assert(s[i]=='_',"no separator _ found");
379             ++i;
380             uint ate;
381             *val=cast(uint)Integer.convert(s[i..i+8],16,&ate);
382             assert(ate==8,"unexpected read size");
383             i+=8;
384         }
385         return i;
386     }
387 }
388
389 /+ like Kiss99, but synchronized, so multiple thread access is ok
390 + (but if you need multiple thread access think about having a random number generator per thread)
391 + This is the engine, *never* use it directly, always use it though a RandomG class
392 +/
393 struct Kiss99Sync{
394     uint kiss_x = 123456789;
395     uint kiss_y = 362436000;
396     uint kiss_z = 521288629;
397     uint kiss_c = 7654321;
398     uint nBytes = 0;
399     uint restB  = 0;
400     Mutex lock;
401    
402     const int canCheckpoint=true;
403     const int canSeed=true;
404    
405     void skip(uint n){
406         for (int i=n;i!=n;--i){
407             next;
408         }
409     }
410     ubyte nextB(){
411         if (nBytes>0) {
412             ubyte res=cast(ubyte)(restB & 0xFF);
413             restB >>= 8;
414             --nBytes;
415             return res;
416         } else {
417             restB=next;
418             ubyte res=cast(ubyte)(restB & 0xFF);
419             restB >>= 8;
420             nBytes=3;
421             return res;
422         }
423     }
424     uint next(){
425         uint res;
426         const ulong a = 698769069UL;
427         ulong t;
428         synchronized(lock){
429             kiss_x = 69069*kiss_x+12345;
430             kiss_y ^= (kiss_y<<13); kiss_y ^= (kiss_y>>17); kiss_y ^= (kiss_y<<5);
431             t = a*kiss_z+kiss_c; kiss_c = (t>>32);
432             kiss_z=cast(uint)t;
433             res=kiss_x+kiss_y+kiss_z;
434         }
435         return res;
436     }
437     ulong nextL(){
438         return ((cast(ulong)next)<<32)+cast(ulong)next;
439     }
440    
441     void seed(uint delegate() r){
442         if (!lock) lock=new Mutex();
443         synchronized(lock){
444             kiss_x = r();
445             for (int i=0;i<100;++i){
446                 kiss_y=r();
447                 if (kiss_y!=0) break;
448             }
449             if (kiss_y==0) kiss_y=362436000;
450             kiss_z=r();
451             /* Don’t really need to seed c as well (is reset after a next),
452                but doing it allows to completely restore a given internal state */
453             kiss_c = r() % 698769069; /* Should be less than 698769069 */
454             nBytes=0;
455             restB=0;
456         }
457     }
458     ///  IWritable implementation
459     void write (IWriter input){
460         synchronized(lock){
461             input(kiss_x)(kiss_y)(kiss_z)(kiss_c)(nBytes)(restB);
462         }
463     }
464     /// IReadable implementation
465     void read (IReader input){
466         synchronized(lock){
467             input(kiss_x)(kiss_y)(kiss_z)(kiss_c)(nBytes)(restB);
468         }
469     }
470     /// writes the current status in a string
471     char[] toString(){
472         char[] res=new char[6+6*9];
473         int i=0;
474         res[i..i+6]="KISS99";
475         i+=6;
476         synchronized(lock){
477             foreach (val;[kiss_x,kiss_y,kiss_z,kiss_c,nBytes,restB]){
478                 res[i]='_';
479                 ++i;
480                 Integer.format(res[i..i+8],val,"x8");
481                 i+=8;
482             }
483         }
484         assert(i==res.length,"unexpected size");
485         return res;
486     }
487     /// reads the current status from a string (that should have been trimmed)
488     /// returns the number of chars read
489     uint fromString(char[] s){
490         uint i=0;
491         assert(s[i..i+4]=="KISS","unexpected kind, expected KISS");
492         assert(s[i+4..i+7]=="99_","unexpected version, expected 99");
493         i+=6;
494         synchronized(lock){
495             foreach (val;[&kiss_x,&kiss_y,&kiss_z,&kiss_c,&nBytes,&restB]){
496                 assert(s[i]=='_',"no separator _ found");
497                 ++i;
498                 uint ate;
499                 *val=cast(uint)Integer.convert(s[i..i+8],16,&ate);
500                 assert(ate==8,"unexpected read size");
501                 i+=8;
502             }
503         }
504         return i;
505     }
506 }
507
508 /// ziggurat method for decreasing distributions.
509 /// Marsaglia, Tsang, Journal of Statistical Software, 2000
510 /// If has negative is true the distribution is assumed to be symmetric with respect to 0,
511 /// otherwise it is assumed to be from 0 to infinity.
512 /// Struct based to avoid extra indirection when wrapped in a class (and it should be wrapped
513 /// in a class and not used directly).
514 /// Call style initialization avoided on purpose (this is a big structure, you don't want to return it)
515 struct Ziggurat(RandG,T,alias probDensityF,alias tailGenerator,bool hasNegative=true){
516     static assert(isFloat!(T),T.stringof~" not acceptable, only floating point variables supported");
517     const int nBlocks=256;
518     T[nBlocks+1] posBlock;
519     T[nBlocks+1] fVal;
520     RandG r;
521     alias Ziggurat SourceT;
522     /// initializes the ziggurat
523     static Ziggurat create(alias invProbDensityF, alias cumProbDensityFCompl)(RandG rGenerator,real xLast=-1.0L,bool check_error=true){
524         /// function to find xLast
525         real findXLast(real xLast){
526             real v=xLast*probDensityF(xLast)+cumProbDensityFCompl(xLast);
527             real fMax=probDensityF(0.0L);
528             real pAtt=xLast;
529             real fAtt=probDensityF(xLast);
530             for (int i=nBlocks-2;i!=0;--i){
531                 fAtt+=v/pAtt;
532                 if (fAtt>fMax) return fAtt+(i-1)*fMax;
533                 pAtt=invProbDensityF(fAtt);
534                 assert(pAtt>=0,"invProbDensityF is supposed to return positive values");
535             }
536             return fAtt+v/pAtt-fMax;
537         }
538         void findBracket(ref real xMin,ref real xMax){
539             real vMin=cumProbDensityFCompl(0.0L)/nBlocks;
540             real pAtt=0.0L;
541             for (int i=1;i<nBlocks;++i){
542                 pAtt+=vMin/probDensityF(pAtt);
543             }
544             real df=findXLast(pAtt);
545             if (df>0) {
546                 // (most likely)
547                 xMin=pAtt;
548                 real vMax=cumProbDensityFCompl(0.0L);
549                 xMax=pAtt+vMax/probDensityF(pAtt);
550             } else {
551                 xMax=pAtt;
552                 xMin=vMin/probDensityF(0.0L);
553             }
554         }
555         if (xLast<=0){
556             real xMin,xMax;
557             findBracket(xMin,xMax);
558             xLast=findRoot(&findXLast,xMin,xMax);
559             // printf("xLast:%La => %La\n",xLast,findXLast(xLast));
560         }
561         Ziggurat res;
562         with (res){
563             r=rGenerator;
564             real v=probDensityF(xLast)*xLast+cumProbDensityFCompl(xLast);
565             real pAtt=xLast;
566             real fMax=probDensityF(0.0L);
567             posBlock[1]=cast(T)xLast;
568             real fAtt=probDensityF(xLast);
569             fVal[1]=cast(T)fAtt;
570             for (int i=2;i<nBlocks;++i){
571                 fAtt+=v/pAtt;
572                 assert(fAtt<=fMax,"Ziggurat contruction shoot out");
573                 pAtt=invProbDensityF(fAtt);
574                 assert<