Skip to content

Random number generation classes #2793

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 43 additions & 53 deletions libs/libvtrutil/src/specrand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,32 +52,15 @@

#include "specrand.h"

/* Period parameters */
#define N 624
#define M 397
#define MATRIX_A 0x9908b0dfUL /* constant vector a */
#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
#define LOWER_MASK 0x7fffffffUL /* least significant r bits */

static unsigned long mt[N]; /* the array for the state vector */
static int mti = N + 1; /* mti==N+1 means mt[N] is not initialized */

void spec_srand(int seed) {
spec_init_genrand((unsigned long)seed);
double SpecRandomNumberGenerator::spec_rand_() {
return spec_genrand_int32_() * (1.0 / 4294967296.0);
}

/* Just a copy of spec_genrand_real2() */
double spec_rand() {
return spec_genrand_int32() * (1.0 / 4294967296.0);
long SpecRandomNumberGenerator::spec_lrand48_() {
return (long)(spec_genrand_int32_() >> 1);
}

/* Just a copy of spec_genrand_int31() */
long spec_lrand48() {
return (long)(spec_genrand_int32() >> 1);
}

/* initializes mt[N] with a seed */
void spec_init_genrand(unsigned long s) {
void SpecRandomNumberGenerator::spec_init_genrand_(unsigned long s) {
mt[0] = s & 0xffffffffUL;
for (mti = 1; mti < N; mti++) {
mt[mti] = (1812433253UL * (mt[mti - 1] ^ (mt[mti - 1] >> 30)) + mti);
Expand All @@ -90,16 +73,11 @@ void spec_init_genrand(unsigned long s) {
}
}

/* initialize by an array with array-length */
/* init_key is the array for initializing keys */
/* key_length is its length */
/* slight change for C++, 2004/2/26 */
void spec_init_by_array(unsigned long init_key[], int key_length) {
int i, j, k;
spec_init_genrand(19650218UL);
i = 1;
j = 0;
k = (N > key_length ? N : key_length);
void SpecRandomNumberGenerator::spec_init_by_array_(const unsigned long init_key[], size_t key_length) {
spec_init_genrand_(19650218UL);
size_t i = 1;
size_t j = 0;
size_t k = (N > key_length ? N : key_length);
for (; k; k--) {
mt[i] = (mt[i] ^ ((mt[i - 1] ^ (mt[i - 1] >> 30)) * 1664525UL))
+ init_key[j] + j; /* non linear */
Expand Down Expand Up @@ -127,22 +105,21 @@ void spec_init_by_array(unsigned long init_key[], int key_length) {
}

/* generates a random number on [0,0xffffffff]-interval */
unsigned long spec_genrand_int32() {
unsigned long SpecRandomNumberGenerator::spec_genrand_int32_() {
unsigned long y;
static unsigned long mag01[2] = {0x0UL, MATRIX_A};
/* mag01[x] = x * MATRIX_A for x=0,1 */

if (mti >= N) { /* generate N words at one time */
int kk;

if (mti == N + 1) /* if init_genrand() has not been called, */
spec_init_genrand(5489UL); /* a default initial seed is used */
spec_init_genrand_(5489UL); /* a default initial seed is used */

for (kk = 0; kk < N - M; kk++) {
for (size_t kk = 0; kk < N - M; kk++) {
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
mt[kk] = mt[kk + M] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
for (; kk < N - 1; kk++) {
for (size_t kk; kk < N - 1; kk++) {
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
Expand All @@ -163,32 +140,45 @@ unsigned long spec_genrand_int32() {
return y;
}

/* generates a random number on [0,0x7fffffff]-interval */
long spec_genrand_int31() {
return (long)(spec_genrand_int32() >> 1);
long SpecRandomNumberGenerator::spec_genrand_int31_() {
return (long)(spec_genrand_int32_() >> 1);
}

/* generates a random number on [0,1]-real-interval */
double spec_genrand_real1() {
return spec_genrand_int32() * (1.0 / 4294967295.0);
double SpecRandomNumberGenerator::spec_genrand_real1_() {
return spec_genrand_int32_() * (1.0 / 4294967295.0);
/* divided by 2^32-1 */
}

/* generates a random number on [0,1)-real-interval */
double spec_genrand_real2() {
return spec_genrand_int32() * (1.0 / 4294967296.0);
double SpecRandomNumberGenerator::spec_genrand_real2_() {
return spec_genrand_int32_() * (1.0 / 4294967296.0);
/* divided by 2^32 */
}

/* generates a random number on (0,1)-real-interval */
double spec_genrand_real3() {
return (((double)spec_genrand_int32()) + 0.5) * (1.0 / 4294967296.0);
double SpecRandomNumberGenerator::spec_genrand_real3_() {
return (((double)spec_genrand_int32_()) + 0.5) * (1.0 / 4294967296.0);
/* divided by 2^32 */
}

/* generates a random number on [0,1) with 53-bit resolution*/
double spec_genrand_res53() {
unsigned long a = spec_genrand_int32() >> 5, b = spec_genrand_int32() >> 6;
double SpecRandomNumberGenerator::spec_genrand_res53_() {
unsigned long a = spec_genrand_int32_() >> 5, b = spec_genrand_int32_() >> 6;
return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0);
}
/* These real versions are due to Isaku Wada, 2002/01/09 added */

void SpecRandomNumberGenerator::srandom(int seed) {
spec_init_genrand_((unsigned long)seed);
}

int SpecRandomNumberGenerator::irand(int imax) {
return (int)(spec_genrand_int31_() % (imax + 1));
}

float SpecRandomNumberGenerator::frand() {
return (float)spec_genrand_real2_();
}

SpecRandomNumberGenerator::SpecRandomNumberGenerator(int seed) {
spec_init_genrand_((unsigned long)seed);
}

SpecRandomNumberGenerator::SpecRandomNumberGenerator()
: SpecRandomNumberGenerator(0) {}
78 changes: 56 additions & 22 deletions libs/libvtrutil/src/specrand.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,35 +52,69 @@
/* Slightly modified for use in SPEC CPU by Cloyce D. Spradling (5 Nov 2009)
*/

void spec_srand(int seed);
double spec_rand();
long spec_lrand48();
#include "vtr_random.h"

/* initializes mt[N] with a seed */
void spec_init_genrand(unsigned long s);
class SpecRandomNumberGenerator : public vtr::RandomNumberGeneratorInterface {
public:
SpecRandomNumberGenerator(const SpecRandomNumberGenerator&) = delete;
SpecRandomNumberGenerator& operator=(SpecRandomNumberGenerator& other) = delete;

/* initialize by an array with array-length */
/* init_key is the array for initializing keys */
/* key_length is its length */
/* slight change for C++, 2004/2/26 */
void spec_init_by_array(unsigned long init_key[], int key_length);
SpecRandomNumberGenerator();
explicit SpecRandomNumberGenerator(int seed);

/* generates a random number on [0,0xffffffff]-interval */
unsigned long spec_genrand_int32();
virtual void srandom(int seed) override;
virtual int irand(int imax) override;
virtual float frand() override;

/* generates a random number on [0,0x7fffffff]-interval */
long spec_genrand_int31();
private:
/// @brief initializes mt[N] with a seed
void spec_init_genrand_(unsigned long s);

/* generates a random number on [0,1]-real-interval */
double spec_genrand_real1();
/**
* @brief initialize by an array with array-length
* @param init_key the array for initializing keys
* @param key_length the length of array
*/
void spec_init_by_array_(const unsigned long init_key[], size_t key_length);

/* generates a random number on [0,1)-real-interval */
double spec_genrand_real2();
/// @brief generates a random number on [0,0xffffffff]-interval
unsigned long spec_genrand_int32_();

/* generates a random number on (0,1)-real-interval */
double spec_genrand_real3();
/// @brief Just a copy of spec_genrand_real2()
double spec_rand_();
/// @brief Just a copy of spec_genrand_int31()
long spec_lrand48_();

/* generates a random number on [0,1) with 53-bit resolution*/
double spec_genrand_res53();
/// @brief generates a random number on [0,0x7fffffff]-interval */
long spec_genrand_int31_();

/// @brief generates a random number on [0,1]-real-interval
double spec_genrand_real1_();

/// @brief generates a random number on [0,1)-real-interval
double spec_genrand_real2_();

/// @brief generates a random number on (0,1)-real-interval
double spec_genrand_real3_();

/// @brief generates a random number on [0,1) with 53-bit resolution
double spec_genrand_res53_();

private:
/// Period parameters
static constexpr size_t M = 397;
static constexpr size_t N = 624;
/// constant vector a
static constexpr unsigned long MATRIX_A = 0x9908b0dfUL;
/// most significant w-r bits
static constexpr unsigned long UPPER_MASK = 0x80000000UL;
/// least significant r bits
static constexpr unsigned long LOWER_MASK = 0x7fffffffUL;
/// mti==N+1 means mt[N] is not initialized
size_t mti = N + 1;
/// the array for the state vector
unsigned long mt[N];

};

#endif
102 changes: 39 additions & 63 deletions libs/libvtrutil/src/vtr_random.cpp
Original file line number Diff line number Diff line change
@@ -1,91 +1,67 @@
#include <cstddef>

#include "vtr_random.h"
#include "specrand.h"
#include "vtr_util.h"
#include "vtr_error.h"
#include "specrand.h"

#define CHECK_RAND

namespace vtr {
/* Portable random number generator defined below. Taken from ANSI C by *
* K & R. Not a great generator, but fast, and good enough for my needs. */

constexpr size_t IA = 1103515245u;
constexpr size_t IC = 12345u;
constexpr size_t IM = 2147483648u;

static RandState random_state = 0;

/**
* @brief The pseudo-random number generator is initialized using the argument passed as seed.
*/
void srandom(int seed) {
random_state = (unsigned int)seed;
#ifdef SPEC_CPU
/* SPEC CPU requires a different random number generator */
spec_init_genrand((unsigned long)seed);
#endif
RandomNumberGenerator::RandomNumberGenerator(int seed) {
random_state_ = (unsigned int)seed;
}

/* returns the random_state value */
RandState get_random_state() {
return random_state;
RandomNumberGenerator::RandomNumberGenerator()
: RandomNumberGenerator(0) {}

void RandomNumberGenerator::srandom(int seed) {
random_state_ = (unsigned int)seed;
}

int irand(int imax, RandState& state) {
#ifdef SPEC_CPU
/* SPEC CPU requires a different random number generator */
return (int)(spec_genrand_int31() % (imax + 1));
#else
/* Creates a random integer between 0 and imax, inclusive. i.e. [0..imax] */
int RandomNumberGenerator::irand(int imax) {
// Creates a random integer between 0 and imax, inclusive. i.e. [0..imax]
int ival;

/* state = (state * IA + IC) % IM; */
state = state * IA + IC; /* Use overflow to wrap */
ival = state & (IM - 1); /* Modulus */
// state = (state * IA + IC) % IM;
random_state_ = random_state_ * IA + IC; // Use overflow to wrap
ival = random_state_ & (IM - 1); // Modulus
ival = (int)((float)ival * (float)(imax + 0.999) / (float)IM);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could/should we just use the SPEC random number generator all the time? I put in the original random number generator, but if the SPEC one has better randomness and isn't noticeably slower we could just make these routines call it.

I don't have a really strong opinion about this, but maybe we can simplify a code path.

Copy link
Contributor Author

@soheilshahrouz soheilshahrouz Nov 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am concerned that if we use SPEC random generator, we need to update golden results for a large number of tests. We can explore the performance/quality trade-off of these two random generators later.


# ifdef CHECK_RAND
if ((ival < 0) || (ival > imax)) {
if (ival == imax + 1) {
/* Due to random floating point rounding, sometimes above calculation gives number greater than ival by 1 */
ival = imax;
} else {
throw VtrError(string_fmt("Bad value in my_irand, imax = %d ival = %d", imax, ival), __FILE__, __LINE__);
if constexpr (CHECK_RAND_CONSTEXPR) {
if ((ival < 0) || (ival > imax)) {
if (ival == imax + 1) {
// Due to random floating point rounding, sometimes above calculation gives number greater than ival by 1
ival = imax;
} else {
throw VtrError(string_fmt("Bad value in my_irand, imax = %d ival = %d", imax, ival), __FILE__, __LINE__);
}
}
}
# endif

return ival;
#endif
}

int irand(int imax) {
return irand(imax, random_state);
}
float RandomNumberGenerator::frand() {
random_state_ = random_state_ * IA + IC; /* Use overflow to wrap */
int ival = random_state_ & (IM - 1); /* Modulus */
float fval = (float)ival / (float)IM;

float frand() {
/* Creates a random float between 0 and 1. i.e. [0..1). */
#ifdef SPEC_CPU
/* SPEC CPU requires a different random number generator */
return (float)spec_genrand_real2();
#else
float fval;
int ival;
if constexpr (CHECK_RAND_CONSTEXPR) {
if (fval < 0 || fval > 1.) {
throw VtrError(string_fmt("Bad value in my_frand, fval = %g", fval), __FILE__, __LINE__);
}
}

random_state = random_state * IA + IC; /* Use overflow to wrap */
ival = random_state & (IM - 1); /* Modulus */
fval = (float)ival / (float)IM;
return fval;
}

# ifdef CHECK_RAND
if ((fval < 0) || (fval > 1.)) {
throw VtrError(string_fmt("Bad value in my_frand, fval = %g", fval), __FILE__, __LINE__);
RngContainer::RngContainer(int seed) {
if constexpr (SPEC_CPU_CONSTEXPR) {
rng_ = std::make_unique<SpecRandomNumberGenerator>(seed);
} else {
rng_ = std::make_unique<vtr::RandomNumberGenerator>(seed);
}
# endif

return (fval);
#endif
}

RngContainer::RngContainer()
: RngContainer(0) {}
} // namespace vtr
Loading
Loading