//@HEADER // ************************************************************************ // // Kokkos v. 2.0 // Copyright (2014) Sandia Corporation // // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, // the U.S. Government retains certain rights in this software. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. Neither the name of the Corporation nor the names of the // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Questions? Contact Christian R. Trott (crtrott@sandia.gov) // // ************************************************************************ //@HEADER #ifndef KOKKOS_TEST_DUALVIEW_HPP #define KOKKOS_TEST_DUALVIEW_HPP #include #include #include #include #include #include #include #include #include namespace Test { namespace Impl{ // This test runs the random number generators and uses some statistic tests to // check the 'goodness' of the random numbers: // (i) mean: the mean is expected to be 0.5*RAND_MAX // (ii) variance: the variance is 1/3*mean*mean // (iii) covariance: the covariance is 0 // (iv) 1-tupledistr: the mean, variance and covariance of a 1D Histrogram of random numbers // (v) 3-tupledistr: the mean, variance and covariance of a 3D Histrogram of random numbers #define HIST_DIM3D 24 #define HIST_DIM1D (HIST_DIM3D*HIST_DIM3D*HIST_DIM3D) struct RandomProperties { uint64_t count; double mean; double variance; double covariance; double min; double max; KOKKOS_INLINE_FUNCTION RandomProperties() { count = 0; mean = 0.0; variance = 0.0; covariance = 0.0; min = 1e64; max = -1e64; } KOKKOS_INLINE_FUNCTION RandomProperties& operator+=(const RandomProperties& add) { count += add.count; mean += add.mean; variance += add.variance; covariance += add.covariance; min = add.minmax?add.max:max; return *this; } KOKKOS_INLINE_FUNCTION void operator+=(const volatile RandomProperties& add) volatile { count += add.count; mean += add.mean; variance += add.variance; covariance += add.covariance; min = add.minmax?add.max:max; } }; template struct test_random_functor { typedef typename GeneratorPool::generator_type rnd_type; typedef RandomProperties value_type; typedef typename GeneratorPool::device_type device_type; GeneratorPool rand_pool; const double mean; // NOTE (mfh 03 Nov 2014): Kokkos::rand::max() is supposed to define // an exclusive upper bound on the range of random numbers that // draw() can generate. However, for the float specialization, some // implementations might violate this upper bound, due to rounding // error. Just in case, we leave an extra space at the end of each // dimension, in the View types below. typedef Kokkos::View type_1d; type_1d density_1d; typedef Kokkos::View type_3d; type_3d density_3d; test_random_functor (GeneratorPool rand_pool_, type_1d d1d, type_3d d3d) : rand_pool (rand_pool_), mean (0.5*Kokkos::rand::max ()), density_1d (d1d), density_3d (d3d) {} KOKKOS_INLINE_FUNCTION void operator() (int i, RandomProperties& prop) const { using Kokkos::atomic_fetch_add; rnd_type rand_gen = rand_pool.get_state(); for (int k = 0; k < 1024; ++k) { const Scalar tmp = Kokkos::rand::draw(rand_gen); prop.count++; prop.mean += tmp; prop.variance += (tmp-mean)*(tmp-mean); const Scalar tmp2 = Kokkos::rand::draw(rand_gen); prop.count++; prop.mean += tmp2; prop.variance += (tmp2-mean)*(tmp2-mean); prop.covariance += (tmp-mean)*(tmp2-mean); const Scalar tmp3 = Kokkos::rand::draw(rand_gen); prop.count++; prop.mean += tmp3; prop.variance += (tmp3-mean)*(tmp3-mean); prop.covariance += (tmp2-mean)*(tmp3-mean); // NOTE (mfh 03 Nov 2014): Kokkos::rand::max() is supposed to // define an exclusive upper bound on the range of random // numbers that draw() can generate. However, for the float // specialization, some implementations might violate this upper // bound, due to rounding error. Just in case, we have left an // extra space at the end of each dimension of density_1d and // density_3d. // // Please note that those extra entries might not get counted in // the histograms. However, if Kokkos::rand is broken and only // returns values of max(), the histograms will still catch this // indirectly, since none of the other values will be filled in. const Scalar theMax = Kokkos::rand::max (); const uint64_t ind1_1d = static_cast (1.0 * HIST_DIM1D * tmp / theMax); const uint64_t ind2_1d = static_cast (1.0 * HIST_DIM1D * tmp2 / theMax); const uint64_t ind3_1d = static_cast (1.0 * HIST_DIM1D * tmp3 / theMax); const uint64_t ind1_3d = static_cast (1.0 * HIST_DIM3D * tmp / theMax); const uint64_t ind2_3d = static_cast (1.0 * HIST_DIM3D * tmp2 / theMax); const uint64_t ind3_3d = static_cast (1.0 * HIST_DIM3D * tmp3 / theMax); atomic_fetch_add (&density_1d(ind1_1d), 1); atomic_fetch_add (&density_1d(ind2_1d), 1); atomic_fetch_add (&density_1d(ind3_1d), 1); atomic_fetch_add (&density_3d(ind1_3d, ind2_3d, ind3_3d), 1); } rand_pool.free_state(rand_gen); } }; template struct test_histogram1d_functor { typedef RandomProperties value_type; typedef typename DeviceType::execution_space execution_space; typedef typename DeviceType::memory_space memory_space; // NOTE (mfh 03 Nov 2014): Kokkos::rand::max() is supposed to define // an exclusive upper bound on the range of random numbers that // draw() can generate. However, for the float specialization, some // implementations might violate this upper bound, due to rounding // error. Just in case, we leave an extra space at the end of each // dimension, in the View type below. typedef Kokkos::View type_1d; type_1d density_1d; double mean; test_histogram1d_functor (type_1d d1d, int num_draws) : density_1d (d1d), mean (1.0*num_draws/HIST_DIM1D*3) { } KOKKOS_INLINE_FUNCTION void operator() (const typename memory_space::size_type i, RandomProperties& prop) const { typedef typename memory_space::size_type size_type; const double count = density_1d(i); prop.mean += count; prop.variance += 1.0 * (count - mean) * (count - mean); //prop.covariance += 1.0*count*count; prop.min = count < prop.min ? count : prop.min; prop.max = count > prop.max ? count : prop.max; if (i < static_cast (HIST_DIM1D-1)) { prop.covariance += (count - mean) * (density_1d(i+1) - mean); } } }; template struct test_histogram3d_functor { typedef RandomProperties value_type; typedef typename DeviceType::execution_space execution_space; typedef typename DeviceType::memory_space memory_space; // NOTE (mfh 03 Nov 2014): Kokkos::rand::max() is supposed to define // an exclusive upper bound on the range of random numbers that // draw() can generate. However, for the float specialization, some // implementations might violate this upper bound, due to rounding // error. Just in case, we leave an extra space at the end of each // dimension, in the View type below. typedef Kokkos::View type_3d; type_3d density_3d; double mean; test_histogram3d_functor (type_3d d3d, int num_draws) : density_3d (d3d), mean (1.0*num_draws/HIST_DIM1D) {} KOKKOS_INLINE_FUNCTION void operator() (const typename memory_space::size_type i, RandomProperties& prop) const { typedef typename memory_space::size_type size_type; const double count = density_3d(i/(HIST_DIM3D*HIST_DIM3D), (i % (HIST_DIM3D*HIST_DIM3D))/HIST_DIM3D, i % HIST_DIM3D); prop.mean += count; prop.variance += (count - mean) * (count - mean); if (i < static_cast (HIST_DIM1D-1)) { const double count_next = density_3d((i+1)/(HIST_DIM3D*HIST_DIM3D), ((i+1)%(HIST_DIM3D*HIST_DIM3D))/HIST_DIM3D, (i+1)%HIST_DIM3D); prop.covariance += (count - mean) * (count_next - mean); } } }; // // Templated test that uses the above functors. // template struct test_random_scalar { typedef typename RandomGenerator::generator_type rnd_type; int pass_mean,pass_var,pass_covar; int pass_hist1d_mean,pass_hist1d_var,pass_hist1d_covar; int pass_hist3d_mean,pass_hist3d_var,pass_hist3d_covar; test_random_scalar (typename test_random_functor::type_1d& density_1d, typename test_random_functor::type_3d& density_3d, RandomGenerator& pool, unsigned int num_draws) { using std::cout; using std::endl; using Kokkos::parallel_reduce; { cout << " -- Testing randomness properties" << endl; RandomProperties result; typedef test_random_functor functor_type; parallel_reduce (num_draws/1024, functor_type (pool, density_1d, density_3d), result); //printf("Result: %lf %lf %lf\n",result.mean/num_draws/3,result.variance/num_draws/3,result.covariance/num_draws/2); double tolerance = 1.6*std::sqrt(1.0/num_draws); double mean_expect = 0.5*Kokkos::rand::max(); double variance_expect = 1.0/3.0*mean_expect*mean_expect; double mean_eps = mean_expect/(result.mean/num_draws/3)-1.0; double variance_eps = variance_expect/(result.variance/num_draws/3)-1.0; double covariance_eps = result.covariance/num_draws/2/variance_expect; pass_mean = ((-tolerance < mean_eps) && ( tolerance > mean_eps)) ? 1:0; pass_var = ((-1.5*tolerance < variance_eps) && ( 1.5*tolerance > variance_eps)) ? 1:0; pass_covar = ((-2.0*tolerance < covariance_eps) && ( 2.0*tolerance > covariance_eps)) ? 1:0; cout << "Pass: " << pass_mean << " " << pass_var << " " << mean_eps << " " << variance_eps << " " << covariance_eps << " || " << tolerance << endl; } { cout << " -- Testing 1-D histogram" << endl; RandomProperties result; typedef test_histogram1d_functor functor_type; parallel_reduce (HIST_DIM1D, functor_type (density_1d, num_draws), result); double tolerance = 6*std::sqrt(1.0/HIST_DIM1D); double mean_expect = 1.0*num_draws*3/HIST_DIM1D; double variance_expect = 1.0*num_draws*3/HIST_DIM1D*(1.0-1.0/HIST_DIM1D); double covariance_expect = -1.0*num_draws*3/HIST_DIM1D/HIST_DIM1D; double mean_eps = mean_expect/(result.mean/HIST_DIM1D)-1.0; double variance_eps = variance_expect/(result.variance/HIST_DIM1D)-1.0; double covariance_eps = (result.covariance/HIST_DIM1D - covariance_expect)/mean_expect; pass_hist1d_mean = ((-0.0001 < mean_eps) && ( 0.0001 > mean_eps)) ? 1:0; pass_hist1d_var = ((-0.07 < variance_eps) && ( 0.07 > variance_eps)) ? 1:0; pass_hist1d_covar = ((-0.06 < covariance_eps) && ( 0.06 > covariance_eps)) ? 1:0; cout << "Density 1D: " << mean_eps << " " << variance_eps << " " << (result.covariance/HIST_DIM1D/HIST_DIM1D) << " || " << tolerance << " " << result.min << " " << result.max << " || " << result.variance/HIST_DIM1D << " " << 1.0*num_draws*3/HIST_DIM1D*(1.0-1.0/HIST_DIM1D) << " || " << result.covariance/HIST_DIM1D << " " << -1.0*num_draws*3/HIST_DIM1D/HIST_DIM1D << endl; } { cout << " -- Testing 3-D histogram" << endl; RandomProperties result; typedef test_histogram3d_functor functor_type; parallel_reduce (HIST_DIM1D, functor_type (density_3d, num_draws), result); double tolerance = 6*std::sqrt(1.0/HIST_DIM1D); double mean_expect = 1.0*num_draws/HIST_DIM1D; double variance_expect = 1.0*num_draws/HIST_DIM1D*(1.0-1.0/HIST_DIM1D); double covariance_expect = -1.0*num_draws/HIST_DIM1D/HIST_DIM1D; double mean_eps = mean_expect/(result.mean/HIST_DIM1D)-1.0; double variance_eps = variance_expect/(result.variance/HIST_DIM1D)-1.0; double covariance_eps = (result.covariance/HIST_DIM1D - covariance_expect)/mean_expect; pass_hist3d_mean = ((-tolerance < mean_eps) && ( tolerance > mean_eps)) ? 1:0; pass_hist3d_var = ((-1.2*tolerance < variance_eps) && ( 1.2*tolerance > variance_eps)) ? 1:0; pass_hist3d_covar = ((-tolerance < covariance_eps) && ( tolerance > covariance_eps)) ? 1:0; cout << "Density 3D: " << mean_eps << " " << variance_eps << " " << result.covariance/HIST_DIM1D/HIST_DIM1D << " || " << tolerance << " " << result.min << " " << result.max << endl; } } }; template void test_random(unsigned int num_draws) { using std::cout; using std::endl; typename test_random_functor::type_1d density_1d("D1d"); typename test_random_functor::type_3d density_3d("D3d"); uint64_t ticks = std::chrono::high_resolution_clock::now().time_since_epoch().count(); cout << "Test Seed:" << ticks << endl; RandomGenerator pool(ticks); cout << "Test Scalar=int" << endl; test_random_scalar test_int(density_1d,density_3d,pool,num_draws); ASSERT_EQ( test_int.pass_mean,1); ASSERT_EQ( test_int.pass_var,1); ASSERT_EQ( test_int.pass_covar,1); ASSERT_EQ( test_int.pass_hist1d_mean,1); ASSERT_EQ( test_int.pass_hist1d_var,1); ASSERT_EQ( test_int.pass_hist1d_covar,1); ASSERT_EQ( test_int.pass_hist3d_mean,1); ASSERT_EQ( test_int.pass_hist3d_var,1); ASSERT_EQ( test_int.pass_hist3d_covar,1); deep_copy(density_1d,0); deep_copy(density_3d,0); cout << "Test Scalar=unsigned int" << endl; test_random_scalar test_uint(density_1d,density_3d,pool,num_draws); ASSERT_EQ( test_uint.pass_mean,1); ASSERT_EQ( test_uint.pass_var,1); ASSERT_EQ( test_uint.pass_covar,1); ASSERT_EQ( test_uint.pass_hist1d_mean,1); ASSERT_EQ( test_uint.pass_hist1d_var,1); ASSERT_EQ( test_uint.pass_hist1d_covar,1); ASSERT_EQ( test_uint.pass_hist3d_mean,1); ASSERT_EQ( test_uint.pass_hist3d_var,1); ASSERT_EQ( test_uint.pass_hist3d_covar,1); deep_copy(density_1d,0); deep_copy(density_3d,0); cout << "Test Scalar=int64_t" << endl; test_random_scalar test_int64(density_1d,density_3d,pool,num_draws); ASSERT_EQ( test_int64.pass_mean,1); ASSERT_EQ( test_int64.pass_var,1); ASSERT_EQ( test_int64.pass_covar,1); ASSERT_EQ( test_int64.pass_hist1d_mean,1); ASSERT_EQ( test_int64.pass_hist1d_var,1); ASSERT_EQ( test_int64.pass_hist1d_covar,1); ASSERT_EQ( test_int64.pass_hist3d_mean,1); ASSERT_EQ( test_int64.pass_hist3d_var,1); ASSERT_EQ( test_int64.pass_hist3d_covar,1); deep_copy(density_1d,0); deep_copy(density_3d,0); cout << "Test Scalar=uint64_t" << endl; test_random_scalar test_uint64(density_1d,density_3d,pool,num_draws); ASSERT_EQ( test_uint64.pass_mean,1); ASSERT_EQ( test_uint64.pass_var,1); ASSERT_EQ( test_uint64.pass_covar,1); ASSERT_EQ( test_uint64.pass_hist1d_mean,1); ASSERT_EQ( test_uint64.pass_hist1d_var,1); ASSERT_EQ( test_uint64.pass_hist1d_covar,1); ASSERT_EQ( test_uint64.pass_hist3d_mean,1); ASSERT_EQ( test_uint64.pass_hist3d_var,1); ASSERT_EQ( test_uint64.pass_hist3d_covar,1); deep_copy(density_1d,0); deep_copy(density_3d,0); cout << "Test Scalar=float" << endl; test_random_scalar test_float(density_1d,density_3d,pool,num_draws); ASSERT_EQ( test_float.pass_mean,1); ASSERT_EQ( test_float.pass_var,1); ASSERT_EQ( test_float.pass_covar,1); ASSERT_EQ( test_float.pass_hist1d_mean,1); ASSERT_EQ( test_float.pass_hist1d_var,1); ASSERT_EQ( test_float.pass_hist1d_covar,1); ASSERT_EQ( test_float.pass_hist3d_mean,1); ASSERT_EQ( test_float.pass_hist3d_var,1); ASSERT_EQ( test_float.pass_hist3d_covar,1); deep_copy(density_1d,0); deep_copy(density_3d,0); cout << "Test Scalar=double" << endl; test_random_scalar test_double(density_1d,density_3d,pool,num_draws); ASSERT_EQ( test_double.pass_mean,1); ASSERT_EQ( test_double.pass_var,1); ASSERT_EQ( test_double.pass_covar,1); ASSERT_EQ( test_double.pass_hist1d_mean,1); ASSERT_EQ( test_double.pass_hist1d_var,1); ASSERT_EQ( test_double.pass_hist1d_covar,1); ASSERT_EQ( test_double.pass_hist3d_mean,1); ASSERT_EQ( test_double.pass_hist3d_var,1); ASSERT_EQ( test_double.pass_hist3d_covar,1); } } } // namespace Test #endif //KOKKOS_TEST_UNORDERED_MAP_HPP