xref: /petsc/src/sys/classes/random/impls/random123/random123.c (revision 25ccb61f8710b5a70be33024bed0ebb68a3e7f6f)
1*25ccb61fSToby Isaac #include <../src/sys/classes/random/randomimpl.h>
2*25ccb61fSToby Isaac #include <Random123/threefry.h>
3*25ccb61fSToby Isaac 
4*25ccb61fSToby Isaac /* The structure of the Random123 methods are similar enough that templates could be used to make the other CBRNGs in
5*25ccb61fSToby Isaac  * the package (aes, ars, philox) available, as well as different block sizes.  But threefry4x64 is a good default,
6*25ccb61fSToby Isaac  * and I'd rather get a simple implementation up and working and come back if there's interest. */
7*25ccb61fSToby Isaac typedef struct _n_PetscRandom123
8*25ccb61fSToby Isaac {
9*25ccb61fSToby Isaac   threefry4x64_ctr_t  counter;
10*25ccb61fSToby Isaac   threefry4x64_key_t  key;
11*25ccb61fSToby Isaac   threefry4x64_ctr_t  result;
12*25ccb61fSToby Isaac   PetscInt            count;
13*25ccb61fSToby Isaac }
14*25ccb61fSToby Isaac PetscRandom123;
15*25ccb61fSToby Isaac 
16*25ccb61fSToby Isaac R123_ULONG_LONG PETSCR123_SEED_0 = R123_64BIT(0x615D333D2655FE14);
17*25ccb61fSToby Isaac R123_ULONG_LONG PETSCR123_SEED_1 = R123_64BIT(0xAFF6369B3EE9FE96);
18*25ccb61fSToby Isaac R123_ULONG_LONG PETSCR123_SEED_2 = R123_64BIT(0x5956EBC717B60E07);
19*25ccb61fSToby Isaac R123_ULONG_LONG PETSCR123_SEED_3 = R123_64BIT(0xEE8612A0CBEABFF1);
20*25ccb61fSToby Isaac 
21*25ccb61fSToby Isaac PetscErrorCode PetscRandomSeed_Random123(PetscRandom r)
22*25ccb61fSToby Isaac {
23*25ccb61fSToby Isaac   threefry4x64_ukey_t  ukey;
24*25ccb61fSToby Isaac   PetscRandom123      *r123 = (PetscRandom123 *) r->data;
25*25ccb61fSToby Isaac 
26*25ccb61fSToby Isaac   PetscFunctionBegin;
27*25ccb61fSToby Isaac   ukey.v[0] = (R123_ULONG_LONG) r->seed;
28*25ccb61fSToby Isaac   ukey.v[1] = PETSCR123_SEED_1;
29*25ccb61fSToby Isaac   ukey.v[2] = PETSCR123_SEED_2;
30*25ccb61fSToby Isaac   ukey.v[3] = PETSCR123_SEED_3;
31*25ccb61fSToby Isaac   /* The point of seeding should be that every time the sequence is seeded you get the same output.  In this CBRNG,
32*25ccb61fSToby Isaac    * that means we have to initialize the key and reset the counts */
33*25ccb61fSToby Isaac   r123->key = threefry4x64keyinit(ukey);
34*25ccb61fSToby Isaac   r123->counter.v[0] = 0;
35*25ccb61fSToby Isaac   r123->counter.v[1] = 1;
36*25ccb61fSToby Isaac   r123->counter.v[2] = 2;
37*25ccb61fSToby Isaac   r123->counter.v[3] = 3;
38*25ccb61fSToby Isaac   r123->result = threefry4x64(r123->counter,r123->key);
39*25ccb61fSToby Isaac   r123->count = 0;
40*25ccb61fSToby Isaac   PetscFunctionReturn(0);
41*25ccb61fSToby Isaac }
42*25ccb61fSToby Isaac 
43*25ccb61fSToby Isaac static PetscReal PetscRandom123Step(PetscRandom123 *r123)
44*25ccb61fSToby Isaac {
45*25ccb61fSToby Isaac   PetscReal scale = ((PetscReal) 1.) / (UINT64_MAX + ((PetscReal) 1.));
46*25ccb61fSToby Isaac   PetscReal shift = .5 * scale;
47*25ccb61fSToby Isaac   PetscInt  mod   = (r123->count++) % 4;
48*25ccb61fSToby Isaac   PetscReal ret;
49*25ccb61fSToby Isaac 
50*25ccb61fSToby Isaac   ret = r123->result.v[mod] * scale + shift;
51*25ccb61fSToby Isaac 
52*25ccb61fSToby Isaac   if (mod == 3) {
53*25ccb61fSToby Isaac     r123->counter.v[0] += 4;
54*25ccb61fSToby Isaac     r123->counter.v[1] += 4;
55*25ccb61fSToby Isaac     r123->counter.v[2] += 4;
56*25ccb61fSToby Isaac     r123->counter.v[3] += 4;
57*25ccb61fSToby Isaac     r123->result = threefry4x64(r123->counter,r123->key);
58*25ccb61fSToby Isaac   }
59*25ccb61fSToby Isaac 
60*25ccb61fSToby Isaac   return ret;
61*25ccb61fSToby Isaac }
62*25ccb61fSToby Isaac 
63*25ccb61fSToby Isaac PetscErrorCode PetscRandomGetValue_Random123(PetscRandom r,PetscScalar *val)
64*25ccb61fSToby Isaac {
65*25ccb61fSToby Isaac   PetscRandom123 *r123 = (PetscRandom123 *) r->data;
66*25ccb61fSToby Isaac   PetscScalar     rscal;
67*25ccb61fSToby Isaac 
68*25ccb61fSToby Isaac   PetscFunctionBegin;
69*25ccb61fSToby Isaac #if defined(PETSC_USE_COMPLEX)
70*25ccb61fSToby Isaac   {
71*25ccb61fSToby Isaac     PetscReal re = PetscRandom123Step(r123);
72*25ccb61fSToby Isaac     PetscReal im = PetscRandom123Step(r123);
73*25ccb61fSToby Isaac 
74*25ccb61fSToby Isaac     if (r->iset) {
75*25ccb61fSToby Isaac       re = re * PetscRealPart(r->width) + PetscRealPart(r->low);
76*25ccb61fSToby Isaac       im = im * PetscImaginaryPart(r->width) + PetscImaginaryPart(r->low);
77*25ccb61fSToby Isaac     }
78*25ccb61fSToby Isaac 
79*25ccb61fSToby Isaac     rscal = PetscCMPLX(re,im);
80*25ccb61fSToby Isaac   }
81*25ccb61fSToby Isaac #else
82*25ccb61fSToby Isaac   rscal = PetscRandom123Step(r123);
83*25ccb61fSToby Isaac   if (r->iset) rscal = rscal * r->width + r->low;
84*25ccb61fSToby Isaac #endif
85*25ccb61fSToby Isaac   *val = rscal;
86*25ccb61fSToby Isaac   PetscFunctionReturn(0);
87*25ccb61fSToby Isaac }
88*25ccb61fSToby Isaac 
89*25ccb61fSToby Isaac PetscErrorCode  PetscRandomGetValueReal_Random123(PetscRandom r,PetscScalar *val)
90*25ccb61fSToby Isaac {
91*25ccb61fSToby Isaac   PetscRandom123 *r123 = (PetscRandom123 *) r->data;
92*25ccb61fSToby Isaac   PetscScalar     rreal;
93*25ccb61fSToby Isaac 
94*25ccb61fSToby Isaac   PetscFunctionBegin;
95*25ccb61fSToby Isaac   rreal = PetscRandom123Step(r123);
96*25ccb61fSToby Isaac   if (r->iset) rreal = rreal * PetscRealPart(r->width) + PetscRealPart(r->low);
97*25ccb61fSToby Isaac   *val = rreal;
98*25ccb61fSToby Isaac   PetscFunctionReturn(0);
99*25ccb61fSToby Isaac }
100*25ccb61fSToby Isaac 
101*25ccb61fSToby Isaac PetscErrorCode PetscRandomDestroy_Random123(PetscRandom r)
102*25ccb61fSToby Isaac {
103*25ccb61fSToby Isaac   PetscErrorCode ierr;
104*25ccb61fSToby Isaac 
105*25ccb61fSToby Isaac   PetscFunctionBegin;
106*25ccb61fSToby Isaac   ierr = PetscFree(r->data);CHKERRQ(ierr);
107*25ccb61fSToby Isaac   PetscFunctionReturn(0);
108*25ccb61fSToby Isaac }
109*25ccb61fSToby Isaac 
110*25ccb61fSToby Isaac static struct _PetscRandomOps PetscRandomOps_Values = {
111*25ccb61fSToby Isaac   /* 0 */
112*25ccb61fSToby Isaac   PetscRandomSeed_Random123,
113*25ccb61fSToby Isaac   PetscRandomGetValue_Random123,
114*25ccb61fSToby Isaac   PetscRandomGetValueReal_Random123,
115*25ccb61fSToby Isaac   PetscRandomDestroy_Random123,
116*25ccb61fSToby Isaac   /* 5 */
117*25ccb61fSToby Isaac   0
118*25ccb61fSToby Isaac };
119*25ccb61fSToby Isaac 
120*25ccb61fSToby Isaac /*MC
121*25ccb61fSToby Isaac    PETSCRANDOM123- access to Random123 counter based pseudorandom number generators (currently threefry4x64)
122*25ccb61fSToby Isaac 
123*25ccb61fSToby Isaac    Options Database Keys:
124*25ccb61fSToby Isaac . -random_type <rand,rand48,sprng,random123>
125*25ccb61fSToby Isaac 
126*25ccb61fSToby Isaac   Level: beginner
127*25ccb61fSToby Isaac 
128*25ccb61fSToby Isaac    PETSc must have been ./configure with the option --download-random123 to use
129*25ccb61fSToby Isaac    this random number generator.
130*25ccb61fSToby Isaac 
131*25ccb61fSToby Isaac .seealso: RandomCreate(), RandomSetType(), PETSCRAND, PETSCRAND48, PETSCSPRNG
132*25ccb61fSToby Isaac M*/
133*25ccb61fSToby Isaac 
134*25ccb61fSToby Isaac PETSC_EXTERN PetscErrorCode PetscRandomCreate_Random123(PetscRandom r)
135*25ccb61fSToby Isaac {
136*25ccb61fSToby Isaac   PetscRandom123 *r123;
137*25ccb61fSToby Isaac   PetscErrorCode ierr;
138*25ccb61fSToby Isaac 
139*25ccb61fSToby Isaac   PetscFunctionBegin;
140*25ccb61fSToby Isaac   ierr = PetscNewLog(r,&r123);CHKERRQ(ierr);
141*25ccb61fSToby Isaac   r->data = r123;
142*25ccb61fSToby Isaac   ierr = PetscMemcpy(r->ops,&PetscRandomOps_Values,sizeof(PetscRandomOps_Values));CHKERRQ(ierr);
143*25ccb61fSToby Isaac   ierr = PetscObjectChangeTypeName((PetscObject)r,PETSCRANDOM123);CHKERRQ(ierr);
144*25ccb61fSToby Isaac   ierr = PetscRandomSeed(r);CHKERRQ(ierr);
145*25ccb61fSToby Isaac   PetscFunctionReturn(0);
146*25ccb61fSToby Isaac }
147