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 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 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 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 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 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 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 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 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