Branch data Line data Source code
1 : : // *****************************************************************************
2 : : /*!
3 : : \file src/RNGTest/TestU01Props.hpp
4 : : \copyright 2012-2015 J. Bakosi,
5 : : 2016-2018 Los Alamos National Security, LLC.,
6 : : 2019-2021 Triad National Security, LLC.
7 : : All rights reserved. See the LICENSE file for details.
8 : : \brief TestU01 statistical test properties class
9 : : \details This file defines a generic TestU01 statistical test properties
10 : : class, used to initialize, interface, and evaluate TestU01 RNG statistical
11 : : tests by the TestU01 library.
12 : : */
13 : : // *****************************************************************************
14 : : #ifndef TestU01Props_h
15 : : #define TestU01Props_h
16 : :
17 : : #include <vector>
18 : : #include <functional>
19 : :
20 : : #include "Macro.hpp"
21 : : #include "RNG.hpp"
22 : : #include "Timer.hpp"
23 : : #include "Options/RNG.hpp"
24 : : #include "TestU01Util.hpp"
25 : : #include "TestStack.hpp"
26 : : #include "TestU01Wrappers.hpp"
27 : : #include "WalkerBuildConfig.hpp"
28 : :
29 : : namespace rngtest {
30 : :
31 : : extern TestStack g_testStack;
32 : :
33 : : //! \brief TestU01 properties used to initialize TestU01 tests.
34 : : //! \details This class is used to abstract away the initialization for TestU01
35 : : //! statistical tests. Needed because of the move semantics and variadic
36 : : //! templates, since Charm++ chares do not support these yet. TestU01Props is
37 : : //! therefore not a chare, but TestU01, initialized with a TestU01Props
38 : : //! object, is. Note that TestU01Props still needs to be migratable, i.e.,
39 : : //! defines the pack/unpack operator, as it is an argument to chare TestU01's
40 : : //! constructor.
41 : : template< class Test, //!< Statistical test tag, struct{}
42 : : class Proxy, //!< Host proxy type
43 : : class Result, //!< Results type
44 : : Result* (*Creator)(void), //!< Results creator function pointer type
45 : : void (*Deleter)(Result *), //!< Results deleter function pointer type
46 : : typename... Ts > //!< Extra test runner argument types
47 : : class TestU01Props {
48 : :
49 : : public:
50 : : //! Test extra arguments type
51 : : using Xargs = std::tuple< Ts... >;
52 : : //! Test runner function pointer type
53 : : using RunFn = std::vector<double> (*)( unif01_Gen*, Result*, const Xargs& );
54 : : //! TestU01 results type with a custom deleter by TestU01
55 : : using ResultPtr = TestU01Ptr< Result, Deleter >;
56 : :
57 : : //! \brief Default constructor taking no arguments
58 : : //! \details Required as migratable. Called by Charm++. Initialize what we
59 : : //! can.
60 : 120 : explicit TestU01Props() :
61 : : m_proxy(),
62 : : m_rng( tk::ctr::RNGType::NO_RNG ),
63 : : m_names(),
64 : : m_xargs(),
65 : : m_gen( nullptr ),
66 : 120 : m_runner( g_testStack.TestU01.runner.get<Test>() ),
67 : : m_res( ResultPtr(Creator()) ),
68 [ + - ]: 120 : m_time( 0.0 ) {}
69 : :
70 : : //! \brief Initializer constructor
71 : : //! \details None of the state data is const since the this class is
72 : : //! designed to be migratable over the network by the Charm++ runtime
73 : : //! system.
74 : : //! \param[in] host Host proxy facilitating call-back to host object chare.
75 : : //! \param[in] rng Random number generator ID enum to be tested
76 : : //! \param[in] n Vector of statisical test names (can be more than one
77 : : //! associated with a given test, since a test can contain more than one
78 : : //! statistical test evaluation, yielding multiple p-values)
79 : : //! \param[in] gen Raw function pointer to TestU01 statistical test
80 : : //! \param[in] xargs Extra arguments to test-run
81 : 120 : explicit TestU01Props( Proxy& host,
82 : : tk::ctr::RNGType rng,
83 : : std::vector< std::string >&& n,
84 : : unif01_Gen* gen,
85 : : Ts&&... xargs ) :
86 : : m_proxy( host ),
87 : : m_rng( rng ),
88 : 120 : m_names( std::move(n) ),
89 : : m_xargs( std::forward<Ts>(xargs)... ),
90 : : m_gen( gen ),
91 : 120 : m_runner( g_testStack.TestU01.runner.get<Test>() ),
92 : : m_res( ResultPtr(Creator()) ),
93 [ + - ]: 120 : m_time( 0.0 ) {}
94 : :
95 : : //! Copy assignment
96 : 240 : TestU01Props& operator=( const TestU01Props& x) {
97 : 240 : m_proxy = x.m_proxy;
98 : 240 : m_rng = x.m_rng;
99 : 240 : m_names = x.m_names;
100 : 240 : m_xargs = x.m_xargs;
101 : 240 : m_gen = x.m_gen;
102 : 240 : m_runner = x.m_runner;
103 : 240 : m_res = ResultPtr(Creator());
104 : 240 : m_time = x.m_time;
105 : 240 : return *this;
106 : : }
107 : :
108 : : //! Copy constructor: in terms of copy assignment
109 [ + - ]: 240 : TestU01Props( const TestU01Props& x ) { operator=(x); }
110 : :
111 : : //! Move assignment
112 : : TestU01Props& operator=( TestU01Props&& ) = default;
113 : : //! Move constructor
114 : 360 : TestU01Props( TestU01Props&& ) = default;
115 : :
116 : : /** @name Pack/Unpack: Serialize Term object for Charm++ */
117 : : ///@{
118 : : //! Pack/Unpack serialize member function
119 : : //! \param[in,out] p Charm++'s PUP::er serializer object reference
120 : 360 : void pup( PUP::er& p ) {
121 : 360 : p | m_proxy;
122 : 360 : p | m_rng;
123 : 360 : p | m_names;
124 : 360 : p | m_xargs;
125 [ + + ]: 360 : if (p.isUnpacking()) {
126 : 120 : m_gen = g_testStack.TestU01.generator( m_rng );
127 : 120 : m_runner = g_testStack.TestU01.runner.get< Test >();
128 : 120 : m_res = ResultPtr( Creator() );
129 : : }
130 : 360 : p | m_time;
131 : 360 : }
132 : : //! \brief Pack/Unpack serialize operator|
133 : : //! \param[in,out] p Charm++'s PUP::er serializer object reference
134 : : //! \param[in,out] c TestU01Props object reference
135 : 360 : friend void operator|( PUP::er& p, TestU01Props& c ) { c.pup(p); }
136 : : ///@}
137 : :
138 : : //! Host proxy accessor
139 : : //! \return Host proxy
140 : 320 : Proxy& proxy() noexcept { return m_proxy; }
141 : :
142 : : //! Number of results/test (i.e., p-values) accessor
143 : : //! \return Number of p-values this test yields
144 : 40 : std::size_t npval() const { return m_names.size(); }
145 : :
146 : : //! Test name(s) accessor
147 : : //! \return Vector of test names (there can be more than one)
148 : 40 : std::vector< std::string > names() const { return m_names; }
149 : :
150 : : //! \brief Run test and return its status as a vector of vector of strings.
151 : : //! \details The return type could potentially be a more specific type,
152 : : //! e.g., a struct with fields RNGType, and a vector of strings for the
153 : : //! p-values, and the names. Instead, we return a more generic vector of
154 : : //! vector of strings type. This helps keeping the corresponding argument
155 : : //! to Battery::evaluate() generic, which may come in handy when other
156 : : //! test libraries are added in the future. The return value is thus the
157 : : //! following in a vector of vectors:
158 : : //! - 0: Name(s) of statistics (note that a test may produce more than one
159 : : //! statistics and thus p-values)
160 : : //! - 1: p-value strings: "pass" or "fail, p-value = ..." for all tests run
161 : : //! (note that a test may produce more than one statistics and thus
162 : : //! p-values)
163 : : //! - 2: RNG name used to run the test: sub-vector length = 1
164 : 120 : std::vector< std::vector< std::string > > run() {
165 : : // Run and time statistical test
166 : 120 : tk::Timer timer;
167 [ + - ]: 240 : const auto pvals = m_runner( m_gen, m_res.get(), m_xargs );
168 [ + - ]: 120 : m_time = timer.dsec();
169 : : // Construct status
170 : 120 : std::vector< std::string > pvalstrs;
171 [ + + ]: 300 : for (std::size_t p=0; p<m_names.size(); ++p) {
172 [ + + ]: 180 : if ( fail(pvals[p]) )
173 [ + - ][ + - ]: 7 : pvalstrs.emplace_back( "fail, p-value = " + pval( pvals[p] ) );
[ + - ]
174 : : else
175 [ + - ]: 173 : pvalstrs.emplace_back( "pass" );
176 : : }
177 [ + - ][ + - ]: 840 : return { m_names, pvalstrs, { tk::ctr::RNG().name(m_rng) } };
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + + ]
[ + + ][ - - ]
[ - - ]
178 : : }
179 : :
180 : : //! Test time accessor
181 : : //! \return Time it took to run the test with the associated RNG name
182 : 120 : std::pair< std::string, tk::real > time()
183 [ + - ][ + - ]: 240 : { return { tk::ctr::RNG().name(m_rng), m_time }; }
184 : :
185 : : private:
186 : : //! \brief Pack/Unpack TestU01 external generator pointer
187 : : //! \details Admittedly, the code below is ugly and looks stupid at first
188 : : //! sight. However, this is a translation of runtime information (a
189 : : //! user-selected RNG that this statistical test exercises) to
190 : : //! compile-time information: associating an RNG id from an enum class,
191 : : //! tk::ctr::RNGType::value, to a compile-time constant, underlying_type
192 : : //! value, facilitating a different global-scope TestU01 external
193 : : //! generator with code reuse. Note that createTestU01Gen() must be
194 : : //! global-scope as it is used to create a different external generator to
195 : : //! TestU01, depending on the RNG. Templating createTestU01Gen on the id
196 : : //! enables the compiler to generate a different wrapper for a different
197 : : //! RNG facilitating simultaneous calls to any or all wrappers as they are
198 : : //! unique functions.
199 : : //! \param[in,out] p Charm++'s PUP::er serializer object reference
200 : : //! \param[in,out] g Reference to raw function pointer to TestU01
201 : : //! statistical test
202 : : void pup( [[maybe_unused]] PUP::er& p, unif01_Gen*& g ) {
203 : : using tk::ctr::RNGType;
204 : : using tk::ctr::raw;
205 : : const auto& rngname = tk::ctr::RNG().name(m_rng);
206 : : Assert( !rngname.empty(), "Empty RNG name not allowed");
207 : : Assert( m_rng != RNGType::NO_RNG, "No RNG type not allowed");
208 : : #ifdef HAS_MKL
209 : : if (m_rng == RNGType::MKL_MCG31)
210 : : g = createTestU01Gen< raw(RNGType::MKL_MCG31) >( rngname );
211 : : else if (m_rng == RNGType::MKL_R250)
212 : : g = createTestU01Gen< raw(RNGType::MKL_R250) >( rngname );
213 : : else if (m_rng == RNGType::MKL_MRG32K3A)
214 : : g = createTestU01Gen< raw(RNGType::MKL_MRG32K3A) >( rngname );
215 : : else if (m_rng == RNGType::MKL_MCG59)
216 : : g = createTestU01Gen< raw(RNGType::MKL_MCG59) >( rngname );
217 : : else if (m_rng == RNGType::MKL_WH)
218 : : g = createTestU01Gen< raw(RNGType::MKL_WH) >( rngname );
219 : : else if (m_rng == RNGType::MKL_MT19937)
220 : : g = createTestU01Gen< raw(RNGType::MKL_MT19937) >( rngname );
221 : : else if (m_rng == RNGType::MKL_MT2203)
222 : : g = createTestU01Gen< raw(RNGType::MKL_MT2203) >( rngname );
223 : : else if (m_rng == RNGType::MKL_SFMT19937)
224 : : g = createTestU01Gen< raw(RNGType::MKL_SFMT19937) >( rngname );
225 : : else if (m_rng == RNGType::MKL_SOBOL)
226 : : g = createTestU01Gen< raw(RNGType::MKL_SOBOL) >( rngname );
227 : : else if (m_rng == RNGType::MKL_NIEDERR)
228 : : g = createTestU01Gen< raw(RNGType::MKL_NIEDERR) >( rngname );
229 : : //else if (m_rng == RNGType::MKL_IABSTRACT)
230 : : // g = createTestU01Gen< raw(RNGType::MKL_IABSTRACT) >( rngname );
231 : : //else if (m_rng == RNGType::MKL_DABSTRACT)
232 : : // g = createTestU01Gen< raw(RNGType::MKL_DABSTRACT) >( rngname );
233 : : //else if (m_rng == RNGType::MKL_SABSTRACT)
234 : : // g = createTestU01Gen< raw(RNGType::MKL_SABSTRACT) >( rngname );
235 : : else if (m_rng == RNGType::MKL_NONDETERM)
236 : : g = createTestU01Gen< raw(RNGType::MKL_NONDETERM) >( rngname );
237 : : else
238 : : #endif
239 : : #ifdef HAS_RNGSSE2
240 : : if (m_rng == RNGType::RNGSSE_GM19)
241 : : g = createTestU01Gen< raw(RNGType::RNGSSE_GM19) >( rngname );
242 : : else if (m_rng == RNGType::RNGSSE_GM29)
243 : : g = createTestU01Gen< raw(RNGType::RNGSSE_GM29) >( rngname );
244 : : else if (m_rng == RNGType::RNGSSE_GM31)
245 : : g = createTestU01Gen< raw(RNGType::RNGSSE_GM31) >( rngname );
246 : : else if (m_rng == RNGType::RNGSSE_GM55)
247 : : g = createTestU01Gen< raw(RNGType::RNGSSE_GM55) >( rngname );
248 : : else if (m_rng == RNGType::RNGSSE_GM61)
249 : : g = createTestU01Gen< raw(RNGType::RNGSSE_GM61) >( rngname );
250 : : else if (m_rng == RNGType::RNGSSE_GQ581)
251 : : g = createTestU01Gen< raw(RNGType::RNGSSE_GQ581) >( rngname );
252 : : else if (m_rng == RNGType::RNGSSE_GQ583)
253 : : g = createTestU01Gen< raw(RNGType::RNGSSE_GQ583) >( rngname );
254 : : else if (m_rng == RNGType::RNGSSE_GQ584)
255 : : g = createTestU01Gen< raw(RNGType::RNGSSE_GQ584) >( rngname );
256 : : else if (m_rng == RNGType::RNGSSE_MT19937)
257 : : g = createTestU01Gen< raw(RNGType::RNGSSE_MT19937) >( rngname );
258 : : else if (m_rng == RNGType::RNGSSE_LFSR113)
259 : : g = createTestU01Gen< raw(RNGType::RNGSSE_LFSR113) >( rngname );
260 : : else if (m_rng == RNGType::RNGSSE_MRG32K3A)
261 : : g = createTestU01Gen< raw(RNGType::RNGSSE_MRG32K3A) >( rngname );
262 : : else
263 : : #endif
264 : : if (m_rng == RNGType::R123_THREEFRY)
265 : : g = createTestU01Gen< raw(RNGType::R123_THREEFRY) >( rngname );
266 : : else if (m_rng == RNGType::R123_PHILOX)
267 : : g = createTestU01Gen< raw(RNGType::R123_PHILOX) >( rngname );
268 : : }
269 : :
270 : : //! Query whether test is failed
271 : : //! \param[in] val p-value returned from test
272 : : //! \return true if the RNG has failed the statistical test
273 : 180 : bool fail( double val ) const {
274 [ + + ][ + + ]: 180 : if ((val <= gofw_Suspectp) || (val >= 1.0-gofw_Suspectp))
275 : 7 : return true;
276 : : else
277 : 173 : return false;
278 : : }
279 : :
280 : : //! Return human-readable p-value (ala TestU01::bbattery.c::WritePval)
281 : : //! \param[in] val p-value returned from test
282 : : //! \return Human-readable p-value (ala TestU01::bbattery.c::WritePval)
283 : 7 : std::string pval( double val ) const {
284 [ + - ]: 14 : std::stringstream ss;
285 [ + + ]: 7 : if (val < gofw_Suspectp) {
286 : :
287 [ - + ][ - - ]: 4 : if ((val >= 0.01) && (val <= 0.99))
288 [ - - ]: 0 : ss << val;
289 [ + + ]: 4 : else if (val < gofw_Epsilonp)
290 [ + - ]: 3 : ss << "eps";
291 [ + - ]: 1 : else if (val < 0.01)
292 [ + - ]: 1 : ss << val;
293 [ - - ]: 0 : else if (val >= 1.0 - gofw_Epsilonp1)
294 [ - - ]: 0 : ss << "1 - eps1";
295 [ - - ]: 0 : else if (val < 1.0 - 1.0e-4)
296 [ - - ]: 0 : ss << val;
297 : : else
298 [ - - ]: 0 : ss << 1.0 - val;
299 : :
300 [ + - ]: 3 : } else if (val > 1.0 - gofw_Suspectp) {
301 : :
302 [ + + ]: 3 : if (val >= 1.0 - gofw_Epsilonp1)
303 [ + - ]: 2 : ss << "1 - eps1";
304 [ - + ]: 1 : else if (val >= 1.0 - 1.0e-4)
305 [ - - ][ - - ]: 0 : ss << "1 - " << 1.0 - val;
306 : : else
307 [ + - ]: 1 : ss << val;
308 : :
309 : : }
310 [ + - ]: 14 : return ss.str();
311 : : }
312 : :
313 : : Proxy m_proxy; //!< Host proxy
314 : : tk::ctr::RNGType m_rng; //!< RNG id
315 : : std::vector< std::string > m_names; //!< Name(s) of tests
316 : : Xargs m_xargs; //!< Extra args for run()
317 : : unif01_Gen* m_gen; //!< Pointer to generator
318 : : RunFn m_runner; //!< Test runner function
319 : : ResultPtr m_res; //!< TestU01 results
320 : : tk::real m_time; //!< Test run time measured in seconds
321 : : };
322 : :
323 : : } // rngtest::
324 : :
325 : : #endif // TestU01Props_h
|