/* * Copyright 2008-2013 NVIDIA Corporation * Modifications Copyright© 2019-2025 Advanced Micro Devices, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "test_param_fixtures.hpp" #include "test_utils.hpp" #if !_THRUST_HAS_DEVICE_SYSTEM_STD # include #endif template struct ValidateEngine { THRUST_HOST_DEVICE ValidateEngine(const typename Engine::result_type value_10000) : m_value_10000(value_10000) {} THRUST_HOST_DEVICE bool operator()(void) const { Engine e; e.discard(9999); // get the 10Kth result return e() == m_value_10000; } const typename Engine::result_type m_value_10000; }; // end ValidateEngine template struct ValidateEngineMin { THRUST_HOST_DEVICE bool operator()(void) const { Engine e; bool result = true; for (int i = 0; i < 10000; ++i) { result &= (e() >= Engine::min); } return result; } }; // end ValidateEngineMin template struct ValidateEngineMin { THRUST_HOST_DEVICE bool operator()(void) const { return true; } }; template struct ValidateEngineMax { THRUST_HOST_DEVICE bool operator()(void) const { Engine e; bool result = true; for (int i = 0; i < 10000; ++i) { result &= (e() <= Engine::max); } return result; } }; // end ValidateEngineMax template struct ValidateEngineEqual { THRUST_HOST_DEVICE bool operator()(void) const { bool result = true; // test from default constructor Engine e0, e1; result &= (e0 == e1); // advance engines e0.discard(10000); e1.discard(10000); result &= (e0 == e1); // test from identical seeds Engine e2(13), e3(13); result &= (e2 == e3); // test different seeds aren't equal Engine e4(7), e5(13); result &= !(e4 == e5); // test reseeding engine to the same seed causes equality e4.seed(13); result &= (e4 == e5); return result; } }; template struct ValidateEngineUnequal { THRUST_HOST_DEVICE bool operator()(void) const { bool result = true; // test from default constructor Engine e0, e1; result &= !(e0 != e1); // advance engines e0.discard(1000); e1.discard(1000); result &= !(e0 != e1); // test from identical seeds Engine e2(13), e3(13); result &= !(e2 != e3); // test different seeds aren't equal Engine e4(7), e5(13); result &= (e4 != e5); // test reseeding engine to the same seed causes equality e4.seed(13); result &= !(e4 != e5); // test different discards causes inequality Engine e6(13), e7(13); e6.discard(500); e7.discard(1000); result &= (e6 != e7); return result; } }; template struct ValidateDistributionMin { using random_engine = Engine; THRUST_HOST_DEVICE ValidateDistributionMin(const Distribution& dd) : d(dd) {} THRUST_HOST_DEVICE bool operator()(void) { Engine e; bool result = true; for (int i = 0; i < 10000; ++i) { result &= (d(e) >= d.min()); } return result; } Distribution d; }; template struct ValidateDistributionMax { using random_engine = Engine; THRUST_HOST_DEVICE ValidateDistributionMax(const Distribution& dd) : d(dd) {} THRUST_HOST_DEVICE bool operator()(void) { Engine e; bool result = true; for (int i = 0; i < 10000; ++i) { result &= (d(e) <= d.max()); } return result; } Distribution d; }; template struct ValidateDistributionEqual { THRUST_HOST_DEVICE bool operator()(void) const { return d0 == d1; } Distribution d0, d1; }; template struct ValidateDistributionUnqual { THRUST_HOST_DEVICE bool operator()(void) const { return d0 != d1; } Distribution d0, d1; }; #if THRUST_DEVICE_COMPILER == THRUST_DEVICE_COMPILER_HIP TEST(RandomTests, UsingHip) { SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); ASSERT_EQ(THRUST_DEVICE_SYSTEM, THRUST_DEVICE_SYSTEM_HIP); } #endif template void TestEngineValidation() { // test host thrust::host_vector h(1); thrust::generate(h.begin(), h.end(), ValidateEngine(value_10000)); ASSERT_EQ(true, h[0]); // test device thrust::device_vector d(1); thrust::generate(d.begin(), d.end(), ValidateEngine(value_10000)); ASSERT_EQ(true, d[0]); } template void TestEngineMax() { // test host thrust::host_vector h(1); thrust::generate(h.begin(), h.end(), ValidateEngineMax()); ASSERT_EQ(true, h[0]); // test device thrust::device_vector d(1); thrust::generate(d.begin(), d.end(), ValidateEngineMax()); ASSERT_EQ(true, d[0]); } template void TestEngineMin() { // test host thrust::host_vector h(1); thrust::generate(h.begin(), h.end(), ValidateEngineMin()); ASSERT_EQ(true, h[0]); // test device thrust::device_vector d(1); thrust::generate(d.begin(), d.end(), ValidateEngineMin()); ASSERT_EQ(true, d[0]); } template void TestEngineSaveRestore() { // create a default engine Engine e0; // run it for a while e0.discard(10000); // save it std::stringstream ss; ss << e0; // run it a while longer e0.discard(10000); // restore old state Engine e1; ss >> e1; // run e1 a while longer e1.discard(10000); // both should return the same result ASSERT_EQ(e0(), e1()); } template void TestEngineEqual() { ValidateEngineEqual f; // test host thrust::host_vector h(1); thrust::generate(h.begin(), h.end(), f); ASSERT_EQ(true, h[0]); // test device thrust::device_vector d(1); thrust::generate(d.begin(), d.end(), f); ASSERT_EQ(true, d[0]); } template void TestEngineUnequal() { ValidateEngineUnequal f; // test host thrust::host_vector h(1); thrust::generate(h.begin(), h.end(), f); ASSERT_EQ(true, h[0]); // test device thrust::device_vector d(1); thrust::generate(d.begin(), d.end(), f); ASSERT_EQ(true, d[0]); } TEST(RandomTests, TestRanlux24BaseValidation) { using Engine = thrust::random::ranlux24_base; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineValidation(); } TEST(RandomTests, TestRanlux24BaseMin) { using Engine = thrust::random::ranlux24_base; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineMin(); } TEST(RandomTests, TestRanlux24BaseMax) { using Engine = thrust::random::ranlux24_base; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineMax(); } TEST(RandomTests, TestRanlux24BaseSaveRestore) { using Engine = thrust::random::ranlux24_base; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineSaveRestore(); } TEST(RandomTests, TestRanlux24BaseEqual) { using Engine = thrust::random::ranlux24_base; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineEqual(); } TEST(RandomTests, TestRanlux24BaseUnequal) { using Engine = thrust::random::ranlux24_base; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineUnequal(); } TEST(RandomTests, TestRanlux48BaseValidation) { using Engine = thrust::random::ranlux48_base; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineValidation(); } TEST(RandomTests, TestRanlux48BaseMin) { using Engine = thrust::random::ranlux48_base; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineMin(); } TEST(RandomTests, TestRanlux48BaseMax) { using Engine = thrust::random::ranlux48_base; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineMax(); } TEST(RandomTests, TestRanlux48BaseSaveRestore) { using Engine = thrust::random::ranlux48_base; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineSaveRestore(); } TEST(RandomTests, TestRanlux48BaseEqual) { using Engine = thrust::random::ranlux48_base; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineEqual(); } #if defined(__INTEL_COMPILER) && 1800 >= __INTEL_COMPILER TEST(RandomTests, TestRanlux48BaseUnequal) { SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); // ICPC has a known failure with this test. // See nvbug 200414000. GTEST_NONFATAL_FAILURE_("Known failure") } #else TEST(RandomTests, TestRanlux48BaseUnequal) { using Engine = thrust::random::ranlux48_base; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineUnequal(); } #endif TEST(RandomTests, TestMinstdRandValidation) { using Engine = thrust::random::minstd_rand; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineValidation(); } TEST(RandomTests, TestMinstdRandMin) { using Engine = thrust::random::minstd_rand; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineMin(); } TEST(RandomTests, TestMinstdRandMax) { using Engine = thrust::random::minstd_rand; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineMax(); } TEST(RandomTests, TestMinstdRandSaveRestore) { using Engine = thrust::random::minstd_rand; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineSaveRestore(); } TEST(RandomTests, TestMinstdRandEqual) { using Engine = thrust::random::minstd_rand; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineEqual(); } TEST(RandomTests, TestMinstdRandUnequal) { using Engine = thrust::random::minstd_rand; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineUnequal(); } TEST(RandomTests, TestMinstdRand0Validation) { using Engine = thrust::random::minstd_rand0; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineValidation(); } TEST(RandomTests, TestMinstdRand0Min) { using Engine = thrust::random::minstd_rand0; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineMin(); } TEST(RandomTests, TestMinstdRand0Max) { using Engine = thrust::random::minstd_rand0; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineMax(); } TEST(RandomTests, TestMinstdRand0SaveRestore) { using Engine = thrust::random::minstd_rand0; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineSaveRestore(); } TEST(RandomTests, TestMinstdRand0Equal) { using Engine = thrust::random::minstd_rand0; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineEqual(); } TEST(RandomTests, TestMinstdRand0Unequal) { using Engine = thrust::random::minstd_rand0; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineUnequal(); } TEST(RandomTests, TestTaus88Validation) { using Engine = thrust::random::taus88; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineValidation(); } TEST(RandomTests, TestTaus88Min) { using Engine = thrust::random::taus88; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineMin(); } TEST(RandomTests, TestTaus88Max) { using Engine = thrust::random::taus88; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineMax(); } TEST(RandomTests, TestTaus88SaveRestore) { using Engine = thrust::random::taus88; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineSaveRestore(); } TEST(RandomTests, TestTaus88Equal) { using Engine = thrust::random::taus88; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineEqual(); } TEST(RandomTests, TestTaus88Unequal) { using Engine = thrust::random::taus88; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineUnequal(); } TEST(RandomTests, TestRanlux24Validation) { using Engine = thrust::random::ranlux24; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineValidation(); } TEST(RandomTests, TestRanlux24Min) { using Engine = thrust::random::ranlux24; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineMin(); } TEST(RandomTests, TestRanlux24Max) { using Engine = thrust::random::ranlux24; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineMax(); } TEST(RandomTests, TestRanlux24SaveRestore) { using Engine = thrust::random::ranlux24; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineSaveRestore(); } TEST(RandomTests, TestRanlux24Equal) { using Engine = thrust::random::ranlux24; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineEqual(); } TEST(RandomTests, TestRanlux24Unequal) { using Engine = thrust::random::ranlux24; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineUnequal(); } TEST(RandomTests, TestRanlux48Validation) { using Engine = thrust::random::ranlux48; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineValidation(); } TEST(RandomTests, TestRanlux48Min) { using Engine = thrust::random::ranlux48; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineMin(); } TEST(RandomTests, TestRanlux48Max) { using Engine = thrust::random::ranlux48; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineMax(); } TEST(RandomTests, TestRanlux48SaveRestore) { using Engine = thrust::random::ranlux48; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineSaveRestore(); } TEST(RandomTests, TestRanlux48Equal) { using Engine = thrust::random::ranlux48; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineEqual(); } TEST(RandomTests, TestRanlux48Unequal) { using Engine = thrust::random::ranlux48; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestEngineUnequal(); } THRUST_DIAG_PUSH THRUST_DIAG_SUPPRESS_MSVC(4305) // truncation warning template void ValidateDistributionCharacteristic() { using Engine = typename Validator::random_engine; // test default-constructed Distribution // test host thrust::host_vector h(1); thrust::generate(h.begin(), h.end(), Validator(Distribution())); ASSERT_EQ(true, h[0]); // test device thrust::device_vector d(1); thrust::generate(d.begin(), d.end(), Validator(Distribution())); ASSERT_EQ(true, d[0]); // test distribution & engine with comparable ranges // only do this if they have the same result_type if (_THRUST_STD::is_same::value) { // test Distribution with same range as engine // test host thrust::generate(h.begin(), h.end(), Validator(Distribution(Engine::min, Engine::max))); ASSERT_EQ(true, h[0]); // test device thrust::generate(d.begin(), d.end(), Validator(Distribution(Engine::min, Engine::max))); ASSERT_EQ(true, d[0]); // test Distribution with smaller range than engine // test host typename Distribution::result_type engine_range = Engine::max - Engine::min; thrust::generate(h.begin(), h.end(), Validator(Distribution(engine_range / 3, (2 * engine_range) / 3))); ASSERT_EQ(true, h[0]); // test device thrust::generate(d.begin(), d.end(), Validator(Distribution(engine_range / 3, (2 * engine_range) / 3))); ASSERT_EQ(true, d[0]); } // test Distribution with a very small range // test host thrust::generate(h.begin(), h.end(), Validator(Distribution(1, 6))); ASSERT_EQ(true, h[0]); // test device thrust::generate(d.begin(), d.end(), Validator(Distribution(1, 6))); ASSERT_EQ(true, d[0]); } THRUST_DIAG_POP template void TestDistributionSaveRestore() { // create a default distribution Distribution d0(7, 13); // save it std::stringstream ss; ss << d0; // restore old state Distribution d1; ss >> d1; ASSERT_EQ(d0, d1); } TEST(RandomTests, TestUniformIntDistributionMin) { using int_dist = thrust::random::uniform_int_distribution; using uint_dist = thrust::random::uniform_int_distribution; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); ValidateDistributionCharacteristic>(); ValidateDistributionCharacteristic>(); } TEST(RandomTests, TestUniformIntDistributionMax) { using int_dist = thrust::random::uniform_int_distribution; using uint_dist = thrust::random::uniform_int_distribution; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); ValidateDistributionCharacteristic>(); ValidateDistributionCharacteristic>(); } TEST(RandomTests, TestUniformIntDistributionSaveRestore) { using int_dist = thrust::random::uniform_int_distribution; using uint_dist = thrust::random::uniform_int_distribution; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestDistributionSaveRestore(); TestDistributionSaveRestore(); } TEST(RandomTests, TestUniformRealDistributionMin) { using float_dist = thrust::random::uniform_real_distribution; using double_dist = thrust::random::uniform_real_distribution; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); ValidateDistributionCharacteristic>(); ValidateDistributionCharacteristic>(); } TEST(RandomTests, TestUniformRealDistributionMax) { using float_dist = thrust::random::uniform_real_distribution; using double_dist = thrust::random::uniform_real_distribution; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); ValidateDistributionCharacteristic>(); ValidateDistributionCharacteristic>(); } TEST(RandomTests, TestUniformRealDistributionSaveRestore) { using float_dist = thrust::random::uniform_real_distribution; using double_dist = thrust::random::uniform_real_distribution; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestDistributionSaveRestore(); TestDistributionSaveRestore(); } TEST(RandomTests, TestNormalDistributionMin) { using float_dist = thrust::random::normal_distribution; using double_dist = thrust::random::normal_distribution; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); ValidateDistributionCharacteristic>(); ValidateDistributionCharacteristic>(); } TEST(RandomTests, TestNormalDistributionMax) { using float_dist = thrust::random::normal_distribution; using double_dist = thrust::random::normal_distribution; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); ValidateDistributionCharacteristic>(); ValidateDistributionCharacteristic>(); } TEST(RandomTests, TestNormalDistributionSaveRestore) { using float_dist = thrust::random::normal_distribution; using double_dist = thrust::random::normal_distribution; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); TestDistributionSaveRestore(); TestDistributionSaveRestore(); } TEST(RandomTests, erfcinvFunction) { SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); double inf = std::numeric_limits::infinity(); double nan = std::nan("undefined"); double double_inputs[8] = {-3.0, 0.0, 0.0000001, 0.3, 0.7, 1.20, 2.0, 3.0}; // This values are those returned by the // nvidia's double erfcinv(double a) double double_expected_outputs[8] = {nan, inf, 3.76656, 0.732869, 0.272463, -0.179143, -inf, nan}; for (int i = 0; i < 8; i++) { double input = double_inputs[i]; double output = erfcinv(input); if (std::isnan(output)) { ASSERT_EQ(std::isnan(output), std::isnan(double_expected_outputs[i])); } else if ((output == inf) || (output == -inf)) { ASSERT_EQ(output, double_expected_outputs[i]); } else { EXPECT_NEAR(double_expected_outputs[i], output, 0.01); } } float inf_f = std::numeric_limits::infinity(); float nan_f = std::nanf("undefined"); float float_inputs[8] = {-3.0f, 0.0f, 0.0000001f, 0.3f, 0.7f, 1.20f, 2.0f, 3.0f}; // This values are those returned by // the nvidia's float erfcinv(float // a) float float_expected_outputs[8] = {nan_f, inf_f, 3.76656, 0.732869, 0.272463, -0.179144, -inf_f, nan_f}; for (int i = 0; i < 8; i++) { float input = float_inputs[i]; float output = erfcinv(input); if (std::isnan(output)) { ASSERT_EQ(std::isnan(output), std::isnan(float_expected_outputs[i])); } else if ((output == inf_f) || (output == -inf_f)) { ASSERT_EQ(output, float_expected_outputs[i]); } else { EXPECT_NEAR(float_expected_outputs[i], output, 0.01); } } }