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
PetscRandomSeed_Random123(PetscRandom r)1966976f2fSJacob Faibussowitsch static 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;
383ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
3925ccb61fSToby Isaac }
4025ccb61fSToby Isaac
PetscRandom123Step(PetscRandom123 * r123)41d71ae5a4SJacob Faibussowitsch static PetscReal PetscRandom123Step(PetscRandom123 *r123)
42d71ae5a4SJacob Faibussowitsch {
43*dc085601SHansol Suh PetscReal scale = 1. / ((PetscReal)UINT64_MAX + 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
PetscRandomGetValue_Random123(PetscRandom r,PetscScalar * val)6166976f2fSJacob Faibussowitsch static 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;
843ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
8525ccb61fSToby Isaac }
8625ccb61fSToby Isaac
PetscRandomGetValueReal_Random123(PetscRandom r,PetscReal * val)8766976f2fSJacob Faibussowitsch static 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;
963ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
9725ccb61fSToby Isaac }
9825ccb61fSToby Isaac
PetscRandomGetValuesReal_Random123(PetscRandom r,PetscInt n,PetscReal vals[])99523765e9SHansol Suh static PetscErrorCode PetscRandomGetValuesReal_Random123(PetscRandom r, PetscInt n, PetscReal vals[])
100523765e9SHansol Suh {
101523765e9SHansol Suh PetscRandom123 *r123 = (PetscRandom123 *)r->data;
102523765e9SHansol Suh PetscInt peel_start;
103523765e9SHansol Suh PetscInt rem, lim;
104*dc085601SHansol Suh PetscReal scale = 1. / ((PetscReal)UINT64_MAX + 1.);
105523765e9SHansol Suh PetscReal shift = .5 * scale;
106523765e9SHansol Suh PetscRandom123 r123_copy;
107523765e9SHansol Suh
108523765e9SHansol Suh PetscFunctionBegin;
109523765e9SHansol Suh peel_start = (4 - (r123->count % 4)) % 4;
110523765e9SHansol Suh peel_start = PetscMin(n, peel_start);
111523765e9SHansol Suh for (PetscInt i = 0; i < peel_start; i++) PetscCall(PetscRandomGetValueReal(r, &vals[i]));
112523765e9SHansol Suh PetscAssert((r123->count % 4) == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Bad modular arithmetic");
113523765e9SHansol Suh n -= peel_start;
114523765e9SHansol Suh vals += peel_start;
115523765e9SHansol Suh rem = (n % 4);
116523765e9SHansol Suh lim = n - rem;
117523765e9SHansol Suh if (r->iset) {
118523765e9SHansol Suh scale *= PetscRealPart(r->width);
119523765e9SHansol Suh shift *= PetscRealPart(r->width);
120523765e9SHansol Suh shift += PetscRealPart(r->low);
121523765e9SHansol Suh }
122523765e9SHansol Suh r123_copy = *r123;
123523765e9SHansol Suh for (PetscInt i = 0; i < lim; i += 4, vals += 4) {
124523765e9SHansol Suh vals[0] = r123_copy.result.v[0] * scale + shift;
125523765e9SHansol Suh vals[1] = r123_copy.result.v[1] * scale + shift;
126523765e9SHansol Suh vals[2] = r123_copy.result.v[2] * scale + shift;
127523765e9SHansol Suh vals[3] = r123_copy.result.v[3] * scale + shift;
128523765e9SHansol Suh r123_copy.counter.v[0] += 4;
129523765e9SHansol Suh r123_copy.counter.v[1] += 4;
130523765e9SHansol Suh r123_copy.counter.v[2] += 4;
131523765e9SHansol Suh r123_copy.counter.v[3] += 4;
132523765e9SHansol Suh r123_copy.result = threefry4x64(r123->counter, r123->key);
133523765e9SHansol Suh }
134523765e9SHansol Suh r123_copy.count += lim;
135523765e9SHansol Suh *r123 = r123_copy;
136523765e9SHansol Suh for (PetscInt i = 0; i < rem; i++) PetscCall(PetscRandomGetValueReal(r, &vals[i]));
137523765e9SHansol Suh PetscFunctionReturn(PETSC_SUCCESS);
138523765e9SHansol Suh }
139523765e9SHansol Suh
PetscRandomGetValues_Random123(PetscRandom r,PetscInt n,PetscScalar vals[])140523765e9SHansol Suh static PetscErrorCode PetscRandomGetValues_Random123(PetscRandom r, PetscInt n, PetscScalar vals[])
141523765e9SHansol Suh {
142523765e9SHansol Suh PetscFunctionBegin;
143523765e9SHansol Suh #if PetscDefined(USE_COMPLEX)
144523765e9SHansol Suh for (PetscInt i = 0; i < n; i++) PetscCall(PetscRandomGetValue_Random123(r, n, &vals[i]));
145523765e9SHansol Suh #else
146523765e9SHansol Suh PetscCall(PetscRandomGetValuesReal_Random123(r, n, vals));
147523765e9SHansol Suh #endif
148523765e9SHansol Suh PetscFunctionReturn(PETSC_SUCCESS);
149523765e9SHansol Suh }
150523765e9SHansol Suh
PetscRandomDestroy_Random123(PetscRandom r)15166976f2fSJacob Faibussowitsch static PetscErrorCode PetscRandomDestroy_Random123(PetscRandom r)
152d71ae5a4SJacob Faibussowitsch {
15325ccb61fSToby Isaac PetscFunctionBegin;
1549566063dSJacob Faibussowitsch PetscCall(PetscFree(r->data));
1553ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
15625ccb61fSToby Isaac }
15725ccb61fSToby Isaac
15825ccb61fSToby Isaac static struct _PetscRandomOps PetscRandomOps_Values = {
159523765e9SHansol Suh // clang-format off
160267267bdSJacob Faibussowitsch PetscDesignatedInitializer(seed, PetscRandomSeed_Random123),
161267267bdSJacob Faibussowitsch PetscDesignatedInitializer(getvalue, PetscRandomGetValue_Random123),
162267267bdSJacob Faibussowitsch PetscDesignatedInitializer(getvaluereal, PetscRandomGetValueReal_Random123),
163523765e9SHansol Suh PetscDesignatedInitializer(getvalues, PetscRandomGetValues_Random123),
164523765e9SHansol Suh PetscDesignatedInitializer(getvaluesreal, PetscRandomGetValuesReal_Random123),
165267267bdSJacob Faibussowitsch PetscDesignatedInitializer(destroy, PetscRandomDestroy_Random123),
166523765e9SHansol Suh // clang-format on
16725ccb61fSToby Isaac };
16825ccb61fSToby Isaac
16925ccb61fSToby Isaac /*MC
17025ccb61fSToby Isaac PETSCRANDOM123 - access to Random123 counter based pseudorandom number generators (currently threefry4x64)
17125ccb61fSToby Isaac
172c31d2375SBarry Smith Options Database Key:
1731179163eSBarry Smith . -random_type <rand,rand48,sprng,random123> - select the random number generator at runtim
1741179163eSBarry Smith
175c31d2375SBarry Smith Level: beginner
176c31d2375SBarry Smith
1771179163eSBarry Smith Note:
1781179163eSBarry Smith PETSc must be ./configure with the option --download-random123 to use this random number generator.
17925ccb61fSToby Isaac
1801179163eSBarry Smith .seealso: `RandomCreate()`, `RandomSetType()`, `PETSCRAND`, `PETSCRAND48`, `PETSCSPRNG`, `PetscRandomSetFromOptions()`
18125ccb61fSToby Isaac M*/
18225ccb61fSToby Isaac
PetscRandomCreate_Random123(PetscRandom r)183d71ae5a4SJacob Faibussowitsch PETSC_EXTERN PetscErrorCode PetscRandomCreate_Random123(PetscRandom r)
184d71ae5a4SJacob Faibussowitsch {
18525ccb61fSToby Isaac PetscRandom123 *r123;
18625ccb61fSToby Isaac
18725ccb61fSToby Isaac PetscFunctionBegin;
1884dfa11a4SJacob Faibussowitsch PetscCall(PetscNew(&r123));
18925ccb61fSToby Isaac r->data = r123;
190aea10558SJacob Faibussowitsch r->ops[0] = PetscRandomOps_Values;
1919566063dSJacob Faibussowitsch PetscCall(PetscObjectChangeTypeName((PetscObject)r, PETSCRANDOM123));
1929566063dSJacob Faibussowitsch PetscCall(PetscRandomSeed(r));
1933ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
19425ccb61fSToby Isaac }
195