mirror of https://github.com/intel/intel-qs.git
267 lines
9.8 KiB
C++
267 lines
9.8 KiB
C++
#ifndef RANDOM_NUMBER_GENERATOR_TEST_HPP
|
|
#define RANDOM_NUMBER_GENERATOR_TEST_HPP
|
|
|
|
#include <map>
|
|
|
|
#include "../../include/qureg.hpp"
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Test fixture class.
|
|
|
|
class RandomNumberGeneratorTest : public ::testing::Test
|
|
{
|
|
protected:
|
|
|
|
RandomNumberGeneratorTest()
|
|
{ }
|
|
|
|
// just after the 'constructor'
|
|
void SetUp() override
|
|
{
|
|
// All tests are skipped if the rank is dummy.
|
|
if (qhipster::mpi::Environment::IsUsefulRank() == false)
|
|
GTEST_SKIP();
|
|
}
|
|
|
|
// just before the 'destructor'
|
|
// void TearDown() override {}
|
|
|
|
double accepted_error_ = 1e-15;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Test macros:
|
|
|
|
TEST_F(RandomNumberGeneratorTest, BasicUse)
|
|
{
|
|
std::size_t num_samples = 10;
|
|
double random [num_samples];
|
|
qhipster::RandomNumberGenerator<double> rng;
|
|
std::size_t seed = 777;
|
|
rng.SetSeedStreamPtrs(seed);
|
|
|
|
// Generate the samples.
|
|
rng.UniformRandomNumbers(random, num_samples, 0, 1, "local");
|
|
// They should not be the same number.
|
|
ASSERT_TRUE(random[0] != random[num_samples-1]);
|
|
ASSERT_EQ(rng.GetNumGeneratedOrSkippedLocalNumbers(), num_samples);
|
|
ASSERT_EQ(rng.GetNumGeneratedOrSkippedStateNumbers(), 0);
|
|
ASSERT_EQ(rng.GetNumGeneratedOrSkippedPoolNumbers() , 0 );
|
|
|
|
// New stream, it should be a copy of the previous one since seed is the same.
|
|
qhipster::RandomNumberGenerator<double> rng_copy;
|
|
rng_copy.SetSeedStreamPtrs(seed);
|
|
double random_copy [num_samples];
|
|
// Generate the samples.
|
|
rng_copy.UniformRandomNumbers(random_copy, num_samples, 0., 1.);
|
|
for (std::size_t j=0; j<num_samples; ++j)
|
|
ASSERT_EQ(random[j], random_copy[j]);
|
|
ASSERT_EQ(rng_copy.GetNumGeneratedOrSkippedLocalNumbers(), num_samples);
|
|
ASSERT_EQ(rng_copy.GetNumGeneratedOrSkippedStateNumbers(), 0);
|
|
ASSERT_EQ(rng_copy.GetNumGeneratedOrSkippedPoolNumbers() , 0 );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
TEST_F(RandomNumberGeneratorTest, Visualization)
|
|
{
|
|
if (qhipster::mpi::Environment::GetStateRank() != 0)
|
|
GTEST_SKIP();
|
|
|
|
std::size_t num_samples = 20*2000;
|
|
double random [num_samples];
|
|
qhipster::RandomNumberGenerator<double> rng;
|
|
std::size_t seed = 777;
|
|
rng.SetSeedStreamPtrs(seed);
|
|
|
|
// Generate the samples.
|
|
rng.UniformRandomNumbers(random, num_samples, 0., 20., "local");
|
|
std::map<int, int> histogram;
|
|
for (int n = 0; n < num_samples; ++n)
|
|
{
|
|
++histogram[int(random[n])];
|
|
}
|
|
std::cout << "Histogram of 20*2000 numbers drawn uniformly in [0,20[ "
|
|
<< "(each * corresponds to 100 draws)\n";
|
|
for (auto p : histogram)
|
|
{
|
|
std::cout << setw(2) << p.first << ' ' << std::string(p.second / 100, '*') << '\n';
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
TEST_F(RandomNumberGeneratorTest, DifferentSeed)
|
|
{
|
|
std::size_t num_samples = 100;
|
|
double random [num_samples];
|
|
qhipster::RandomNumberGenerator<double> rng;
|
|
std::size_t seed = 717;
|
|
rng.SetSeedStreamPtrs(seed);
|
|
// Generate the samples.
|
|
rng.UniformRandomNumbers(random, num_samples, 0., 1.,"local");
|
|
ASSERT_TRUE(random[0] != random[num_samples-1]);
|
|
|
|
// New stream, it should be different.
|
|
qhipster::RandomNumberGenerator<double> rng_2;
|
|
rng_2.SetSeedStreamPtrs(seed+1);
|
|
double random_2 [num_samples];
|
|
// Generate the samples.
|
|
rng_2.UniformRandomNumbers(random_2, num_samples, 0., 1.);
|
|
// Verify that the two set of numbers have null intersection.
|
|
for (std::size_t j=0; j<num_samples; ++j)
|
|
for (std::size_t k=0; k<num_samples; ++k)
|
|
ASSERT_NE(random[j], random_2[k]);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
TEST_F(RandomNumberGeneratorTest, LargeRandomVectors)
|
|
{
|
|
// A large vector size gave errors with GCC when running on github servers.
|
|
#if defined(__ICC) || defined(__INTEL_COMPILER)
|
|
std::size_t num_samples = (1UL << 22);
|
|
#else
|
|
std::size_t num_samples = (1UL << 16);
|
|
#endif
|
|
double random [num_samples];
|
|
qhipster::RandomNumberGenerator<double> rng;
|
|
std::size_t seed = 717;
|
|
rng.SetSeedStreamPtrs(seed);
|
|
// Generate the samples.
|
|
rng.UniformRandomNumbers(random, num_samples, 0., 1., "local");
|
|
ASSERT_TRUE(random[0] != random[num_samples-1]);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
TEST_F(RandomNumberGeneratorTest, SkipMethod)
|
|
{
|
|
std::size_t half_samples = 40;
|
|
std::size_t num_samples = 2*half_samples;
|
|
double random [num_samples];
|
|
qhipster::RandomNumberGenerator<double> rng;
|
|
std::size_t seed = 717;
|
|
rng.SetSeedStreamPtrs(seed);
|
|
// Generate the samples.
|
|
rng.UniformRandomNumbers(random, num_samples, 0., 1., "local");
|
|
|
|
// New stream, it should corresponds to the second half of the previous stream.
|
|
qhipster::RandomNumberGenerator<double> rng_2;
|
|
double random_2 [half_samples];
|
|
rng_2.SetSeedStreamPtrs(seed);
|
|
rng_2.SkipAhead(half_samples,"local");
|
|
// Generate the samples.
|
|
rng_2.UniformRandomNumbers(random_2, half_samples, 0., 1., "local");
|
|
for (std::size_t j=0; j<half_samples; ++j)
|
|
ASSERT_EQ(random[j+half_samples], random_2[j]);
|
|
|
|
ASSERT_EQ( rng.GetNumGeneratedOrSkippedLocalNumbers(),
|
|
rng_2.GetNumGeneratedOrSkippedLocalNumbers() );
|
|
|
|
// Same test, but generating Gaussian numbers.
|
|
rng.GaussianRandomNumbers(random, num_samples, "local");
|
|
#if 0
|
|
// Preliminary tests seems to indicate that, without MKL, one needs 2 uses of
|
|
// the generator per uniform double and 3 usus for Gaussian double.
|
|
// A systematic study is not required at this point.
|
|
rng_2.SkipAhead(half_samples,"local");
|
|
#else
|
|
rng_2.GaussianRandomNumbers(random_2, half_samples, "local");
|
|
#endif
|
|
rng_2.GaussianRandomNumbers(random_2, half_samples, "local");
|
|
for (std::size_t j=0; j<half_samples; ++j)
|
|
ASSERT_EQ(random[j+half_samples], random_2[j]);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifdef USE_MKL
|
|
|
|
// This test is special: it is to verify that we are counting the number of generated
|
|
// random values correctly. For example, to generate a single Gaussian number, most
|
|
// methods need to consume two uniformly distributed numbers.
|
|
|
|
TEST_F(RandomNumberGeneratorTest, VslGenerator)
|
|
{
|
|
std::size_t quarter_samples = 8;
|
|
std::size_t num_samples = 4*quarter_samples;
|
|
double random [num_samples];
|
|
std::size_t seed = 717;
|
|
int errcode;
|
|
|
|
VSLStreamStatePtr stream;
|
|
errcode = vslNewStream(&stream, VSL_BRNG_MT2203+0, seed);
|
|
assert(errcode == VSL_STATUS_OK);
|
|
errcode = vdRngUniform(VSL_RNG_METHOD_UNIFORM_STD, stream, num_samples, random, 0, 1);
|
|
assert(errcode == VSL_STATUS_OK);
|
|
|
|
// New stream with same seed that will be used to verify alternative to skip.
|
|
double random_2 [quarter_samples];
|
|
VSLStreamStatePtr stream_2;
|
|
errcode = vslNewStream(&stream_2, VSL_BRNG_MT2203+0, seed);
|
|
assert(errcode == VSL_STATUS_OK);
|
|
errcode = vdRngUniform(VSL_RNG_METHOD_UNIFORM_STD, stream_2, quarter_samples,
|
|
random_2, 0., 1.);
|
|
assert(errcode == VSL_STATUS_OK);
|
|
for (std::size_t j=0; j<quarter_samples; ++j)
|
|
ASSERT_EQ(random[j], random_2[j]);
|
|
// Skip one third of the samples by generating uniform random numbers.
|
|
errcode = vdRngUniform(VSL_RNG_METHOD_UNIFORM_STD, stream_2, quarter_samples,
|
|
random_2, 0., 1.);
|
|
assert(errcode == VSL_STATUS_OK);
|
|
// Now generate the numbers again: they should corresponds to the third quarter of random.
|
|
errcode = vdRngUniform(VSL_RNG_METHOD_UNIFORM_STD, stream_2, quarter_samples,
|
|
random_2, 0., 1.);
|
|
assert(errcode == VSL_STATUS_OK);
|
|
for (std::size_t j=0; j<quarter_samples; ++j)
|
|
ASSERT_EQ(random[j+2*quarter_samples], random_2[j]);
|
|
|
|
// Re-initialize stream_2 with same seed.
|
|
errcode = vslNewStream(&stream_2, VSL_BRNG_MT2203+0, seed);
|
|
assert(errcode == VSL_STATUS_OK);
|
|
errcode = vdRngUniform(VSL_RNG_METHOD_UNIFORM_STD, stream_2, quarter_samples,
|
|
random_2, 0., 1.);
|
|
assert(errcode == VSL_STATUS_OK);
|
|
for (std::size_t j=0; j<quarter_samples; ++j)
|
|
ASSERT_EQ(random[j], random_2[j]);
|
|
// Skip two quarters of the samples by generating Gaussian random numbers:
|
|
// each Gaussian number needs two uniformly distributed numbers.
|
|
errcode = vdRngGaussian(VSL_RNG_METHOD_GAUSSIAN_BOXMULLER, stream_2, quarter_samples,
|
|
random_2, 0.,1.);
|
|
assert(errcode == VSL_STATUS_OK);
|
|
// Now generate the numbers again: they should corresponds to the last quarter of random.
|
|
errcode = vdRngUniform(VSL_RNG_METHOD_UNIFORM_STD, stream_2, quarter_samples,
|
|
random_2, 0., 1.);
|
|
assert(errcode == VSL_STATUS_OK);
|
|
for (std::size_t j=0; j<quarter_samples; ++j)
|
|
ASSERT_EQ(random[j+3*quarter_samples], random_2[j]);
|
|
|
|
// Re-initialize stream_2 with same seed.
|
|
errcode = vslNewStream(&stream_2, VSL_BRNG_MT2203+0, seed);
|
|
assert(errcode == VSL_STATUS_OK);
|
|
errcode = vdRngUniform(VSL_RNG_METHOD_UNIFORM_STD, stream_2, quarter_samples,
|
|
random_2, 0., 1.);
|
|
assert(errcode == VSL_STATUS_OK);
|
|
for (std::size_t j=0; j<quarter_samples; ++j)
|
|
ASSERT_EQ(random[j], random_2[j]);
|
|
// Skip one quarter of the samples by generating random integers in [1,5).
|
|
int value;
|
|
for (std::size_t j=0; j<quarter_samples; ++j)
|
|
{
|
|
errcode = viRngUniform(VSL_RNG_METHOD_UNIFORM_STD, stream_2, 1L, &value, 1, 5);
|
|
assert(errcode == VSL_STATUS_OK);
|
|
}
|
|
// Now generate the numbers again: they should corresponds to the third quarter of random.
|
|
errcode = vdRngUniform(VSL_RNG_METHOD_UNIFORM_STD, stream_2, quarter_samples,
|
|
random_2, 0., 1.);
|
|
assert(errcode == VSL_STATUS_OK);
|
|
for (std::size_t j=0; j<quarter_samples; ++j)
|
|
ASSERT_EQ(random[j+2*quarter_samples], random_2[j]);
|
|
}
|
|
#endif
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
#endif // header guard RANDOM_NUMBER_GENERATOR_TEST_HPP
|