xref: /petsc/src/sys/classes/random/impls/random123/random123.c (revision 3ba1676111f5c958fe6c2729b46ca4d523958bb3)
1d6cc7855SJacob Faibussowitsch #include <petsc/private/randomimpl.h>
225ccb61fSToby Isaac #include <Random123/threefry.h>
325ccb61fSToby Isaac 
425ccb61fSToby Isaac /* The structure of the Random123 methods are similar enough that templates could be used to make the other CBRNGs in
525ccb61fSToby Isaac  * the package (aes, ars, philox) available, as well as different block sizes.  But threefry4x64 is a good default,
625ccb61fSToby Isaac  * and I'd rather get a simple implementation up and working and come back if there's interest. */
79371c9d4SSatish Balay typedef struct _n_PetscRandom123 {
825ccb61fSToby Isaac   threefry4x64_ctr_t counter;
925ccb61fSToby Isaac   threefry4x64_key_t key;
1025ccb61fSToby Isaac   threefry4x64_ctr_t result;
1125ccb61fSToby Isaac   PetscInt           count;
129371c9d4SSatish Balay } PetscRandom123;
1325ccb61fSToby Isaac 
1425ccb61fSToby Isaac R123_ULONG_LONG PETSCR123_SEED_0 = R123_64BIT(0x615D333D2655FE14);
1525ccb61fSToby Isaac R123_ULONG_LONG PETSCR123_SEED_1 = R123_64BIT(0xAFF6369B3EE9FE96);
1625ccb61fSToby Isaac R123_ULONG_LONG PETSCR123_SEED_2 = R123_64BIT(0x5956EBC717B60E07);
1725ccb61fSToby Isaac R123_ULONG_LONG PETSCR123_SEED_3 = R123_64BIT(0xEE8612A0CBEABFF1);
1825ccb61fSToby Isaac 
19d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscRandomSeed_Random123(PetscRandom r)
20d71ae5a4SJacob Faibussowitsch {
2125ccb61fSToby Isaac   threefry4x64_ukey_t ukey;
2225ccb61fSToby Isaac   PetscRandom123     *r123 = (PetscRandom123 *)r->data;
2325ccb61fSToby Isaac 
2425ccb61fSToby Isaac   PetscFunctionBegin;
2525ccb61fSToby Isaac   ukey.v[0] = (R123_ULONG_LONG)r->seed;
2625ccb61fSToby Isaac   ukey.v[1] = PETSCR123_SEED_1;
2725ccb61fSToby Isaac   ukey.v[2] = PETSCR123_SEED_2;
2825ccb61fSToby Isaac   ukey.v[3] = PETSCR123_SEED_3;
2925ccb61fSToby Isaac   /* The point of seeding should be that every time the sequence is seeded you get the same output.  In this CBRNG,
3025ccb61fSToby Isaac    * that means we have to initialize the key and reset the counts */
3125ccb61fSToby Isaac   r123->key          = threefry4x64keyinit(ukey);
3225ccb61fSToby Isaac   r123->counter.v[0] = 0;
3325ccb61fSToby Isaac   r123->counter.v[1] = 1;
3425ccb61fSToby Isaac   r123->counter.v[2] = 2;
3525ccb61fSToby Isaac   r123->counter.v[3] = 3;
3625ccb61fSToby Isaac   r123->result       = threefry4x64(r123->counter, r123->key);
3725ccb61fSToby Isaac   r123->count        = 0;
38*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
3925ccb61fSToby Isaac }
4025ccb61fSToby Isaac 
41d71ae5a4SJacob Faibussowitsch static PetscReal PetscRandom123Step(PetscRandom123 *r123)
42d71ae5a4SJacob Faibussowitsch {
4325ccb61fSToby Isaac   PetscReal scale = ((PetscReal)1.) / (UINT64_MAX + ((PetscReal)1.));
4425ccb61fSToby Isaac   PetscReal shift = .5 * scale;
4525ccb61fSToby Isaac   PetscInt  mod   = (r123->count++) % 4;
4625ccb61fSToby Isaac   PetscReal ret;
4725ccb61fSToby Isaac 
4825ccb61fSToby Isaac   ret = r123->result.v[mod] * scale + shift;
4925ccb61fSToby Isaac 
5025ccb61fSToby Isaac   if (mod == 3) {
5125ccb61fSToby Isaac     r123->counter.v[0] += 4;
5225ccb61fSToby Isaac     r123->counter.v[1] += 4;
5325ccb61fSToby Isaac     r123->counter.v[2] += 4;
5425ccb61fSToby Isaac     r123->counter.v[3] += 4;
5525ccb61fSToby Isaac     r123->result = threefry4x64(r123->counter, r123->key);
5625ccb61fSToby Isaac   }
5725ccb61fSToby Isaac 
5825ccb61fSToby Isaac   return ret;
5925ccb61fSToby Isaac }
6025ccb61fSToby Isaac 
61d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscRandomGetValue_Random123(PetscRandom r, PetscScalar *val)
62d71ae5a4SJacob Faibussowitsch {
6325ccb61fSToby Isaac   PetscRandom123 *r123 = (PetscRandom123 *)r->data;
6425ccb61fSToby Isaac   PetscScalar     rscal;
6525ccb61fSToby Isaac 
6625ccb61fSToby Isaac   PetscFunctionBegin;
6725ccb61fSToby Isaac #if defined(PETSC_USE_COMPLEX)
6825ccb61fSToby Isaac   {
6925ccb61fSToby Isaac     PetscReal re = PetscRandom123Step(r123);
7025ccb61fSToby Isaac     PetscReal im = PetscRandom123Step(r123);
7125ccb61fSToby Isaac 
7225ccb61fSToby Isaac     if (r->iset) {
7325ccb61fSToby Isaac       re = re * PetscRealPart(r->width) + PetscRealPart(r->low);
7425ccb61fSToby Isaac       im = im * PetscImaginaryPart(r->width) + PetscImaginaryPart(r->low);
7525ccb61fSToby Isaac     }
7625ccb61fSToby Isaac 
7725ccb61fSToby Isaac     rscal = PetscCMPLX(re, im);
7825ccb61fSToby Isaac   }
7925ccb61fSToby Isaac #else
8025ccb61fSToby Isaac   rscal = PetscRandom123Step(r123);
8125ccb61fSToby Isaac   if (r->iset) rscal = rscal * r->width + r->low;
8225ccb61fSToby Isaac #endif
8325ccb61fSToby Isaac   *val = rscal;
84*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
8525ccb61fSToby Isaac }
8625ccb61fSToby Isaac 
87d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscRandomGetValueReal_Random123(PetscRandom r, PetscReal *val)
88d71ae5a4SJacob Faibussowitsch {
8925ccb61fSToby Isaac   PetscRandom123 *r123 = (PetscRandom123 *)r->data;
904d1a973fSToby Isaac   PetscReal       rreal;
9125ccb61fSToby Isaac 
9225ccb61fSToby Isaac   PetscFunctionBegin;
9325ccb61fSToby Isaac   rreal = PetscRandom123Step(r123);
9425ccb61fSToby Isaac   if (r->iset) rreal = rreal * PetscRealPart(r->width) + PetscRealPart(r->low);
9525ccb61fSToby Isaac   *val = rreal;
96*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
9725ccb61fSToby Isaac }
9825ccb61fSToby Isaac 
99d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscRandomDestroy_Random123(PetscRandom r)
100d71ae5a4SJacob Faibussowitsch {
10125ccb61fSToby Isaac   PetscFunctionBegin;
1029566063dSJacob Faibussowitsch   PetscCall(PetscFree(r->data));
103*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
10425ccb61fSToby Isaac }
10525ccb61fSToby Isaac 
10625ccb61fSToby Isaac static struct _PetscRandomOps PetscRandomOps_Values = {
107267267bdSJacob Faibussowitsch   PetscDesignatedInitializer(seed, PetscRandomSeed_Random123),
108267267bdSJacob Faibussowitsch   PetscDesignatedInitializer(getvalue, PetscRandomGetValue_Random123),
109267267bdSJacob Faibussowitsch   PetscDesignatedInitializer(getvaluereal, PetscRandomGetValueReal_Random123),
110267267bdSJacob Faibussowitsch   PetscDesignatedInitializer(getvalues, NULL),
111267267bdSJacob Faibussowitsch   PetscDesignatedInitializer(getvaluesreal, NULL),
112267267bdSJacob Faibussowitsch   PetscDesignatedInitializer(destroy, PetscRandomDestroy_Random123),
11325ccb61fSToby Isaac };
11425ccb61fSToby Isaac 
11525ccb61fSToby Isaac /*MC
11625ccb61fSToby Isaac    PETSCRANDOM123 - access to Random123 counter based pseudorandom number generators (currently threefry4x64)
11725ccb61fSToby Isaac 
11825ccb61fSToby Isaac    Options Database Keys:
1191179163eSBarry Smith . -random_type <rand,rand48,sprng,random123> - select the random number generator at runtim
1201179163eSBarry Smith 
1211179163eSBarry Smith    Note:
1221179163eSBarry Smith    PETSc must be ./configure with the option --download-random123 to use this random number generator.
12325ccb61fSToby Isaac 
12425ccb61fSToby Isaac   Level: beginner
12525ccb61fSToby Isaac 
1261179163eSBarry Smith .seealso: `RandomCreate()`, `RandomSetType()`, `PETSCRAND`, `PETSCRAND48`, `PETSCSPRNG`, `PetscRandomSetFromOptions()`
12725ccb61fSToby Isaac M*/
12825ccb61fSToby Isaac 
129d71ae5a4SJacob Faibussowitsch PETSC_EXTERN PetscErrorCode PetscRandomCreate_Random123(PetscRandom r)
130d71ae5a4SJacob Faibussowitsch {
13125ccb61fSToby Isaac   PetscRandom123 *r123;
13225ccb61fSToby Isaac 
13325ccb61fSToby Isaac   PetscFunctionBegin;
1344dfa11a4SJacob Faibussowitsch   PetscCall(PetscNew(&r123));
13525ccb61fSToby Isaac   r->data = r123;
1369566063dSJacob Faibussowitsch   PetscCall(PetscMemcpy(r->ops, &PetscRandomOps_Values, sizeof(PetscRandomOps_Values)));
1379566063dSJacob Faibussowitsch   PetscCall(PetscObjectChangeTypeName((PetscObject)r, PETSCRANDOM123));
1389566063dSJacob Faibussowitsch   PetscCall(PetscRandomSeed(r));
139*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
14025ccb61fSToby Isaac }
141