From f3a69cc37211b215a3ce30912f897cf37485410a Mon Sep 17 00:00:00 2001 From: Riley Murray Date: Wed, 3 Jul 2024 16:09:43 -0400 Subject: [PATCH 01/15] remove include guards and use "#pragma once" instead --- RandBLAS/base.hh | 5 +- RandBLAS/config.h.in | 5 +- RandBLAS/dense_skops.hh | 6 +-- RandBLAS/exceptions.hh | 5 -- RandBLAS/random_gen.hh | 4 +- RandBLAS/skge.hh | 7 +-- RandBLAS/sksy.hh | 4 +- RandBLAS/skve.hh | 5 +- RandBLAS/sparse_data/base.hh | 8 +-- RandBLAS/sparse_data/conversions.hh | 5 +- RandBLAS/sparse_data/coo_matrix.hh | 5 +- RandBLAS/sparse_data/coo_spmm_impl.hh | 6 +-- RandBLAS/sparse_data/csc_matrix.hh | 9 ++-- RandBLAS/sparse_data/csc_spmm_impl.hh | 6 +-- RandBLAS/sparse_data/csr_matrix.hh | 5 +- RandBLAS/sparse_data/csr_spmm_impl.hh | 6 +-- RandBLAS/sparse_data/sksp.hh | 6 +-- RandBLAS/sparse_data/spmm_dispatch.hh | 6 +-- RandBLAS/sparse_skops.hh | 6 +-- RandBLAS/util.hh | 77 +++++++++++++++++++++++++-- 20 files changed, 99 insertions(+), 87 deletions(-) diff --git a/RandBLAS/base.hh b/RandBLAS/base.hh index 76280170..62aa456a 100644 --- a/RandBLAS/base.hh +++ b/RandBLAS/base.hh @@ -27,8 +27,7 @@ // POSSIBILITY OF SUCH DAMAGE. // -#ifndef randblas_base_hh -#define randblas_base_hh +#pragma once /// @file @@ -212,5 +211,3 @@ std::ostream &operator<<( } } // end namespace RandBLAS::base - -#endif diff --git a/RandBLAS/config.h.in b/RandBLAS/config.h.in index 0070edb2..128a4959 100644 --- a/RandBLAS/config.h.in +++ b/RandBLAS/config.h.in @@ -1,5 +1,4 @@ -#ifndef RandBLAS_config_h -#define RandBLAS_config_h +#pragma once #define RandBLAS_FULL_VERSION "@RandBLAS_FULL_VERSION@" #define RandBLAS_VERSION_MAJOR @RandBLAS_VERSION_MAJOR@ @@ -53,5 +52,3 @@ // // if you are linking to OpenMP. // - -#endif diff --git a/RandBLAS/dense_skops.hh b/RandBLAS/dense_skops.hh index c7e60e90..ac92ecbd 100644 --- a/RandBLAS/dense_skops.hh +++ b/RandBLAS/dense_skops.hh @@ -26,9 +26,7 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // - -#ifndef randblas_dense_hh -#define randblas_dense_hh +#pragma once #include "RandBLAS/base.hh" #include "RandBLAS/exceptions.hh" @@ -640,5 +638,3 @@ RNGState fill_dense( } } // end namespace RandBLAS - -#endif \ No newline at end of file diff --git a/RandBLAS/exceptions.hh b/RandBLAS/exceptions.hh index 15c51405..898f0bbe 100644 --- a/RandBLAS/exceptions.hh +++ b/RandBLAS/exceptions.hh @@ -29,9 +29,6 @@ #pragma once -#ifndef RandBLAS_EXCEPTIONS_HH -#define RandBLAS_EXCEPTIONS_HH - #include #include #include @@ -164,5 +161,3 @@ inline void abort_if( bool cond, const char* func, const char* format, ... ) { #endif } // namespace RandBLAS::exceptions - -#endif \ No newline at end of file diff --git a/RandBLAS/random_gen.hh b/RandBLAS/random_gen.hh index 2bec35c4..d7dcf127 100644 --- a/RandBLAS/random_gen.hh +++ b/RandBLAS/random_gen.hh @@ -27,8 +27,7 @@ // POSSIBILITY OF SUCH DAMAGE. // -#ifndef random_gen_hh -#define random_gen_hh +#pragma once /// @file @@ -178,4 +177,3 @@ struct uneg11 } // end of namespace r123ext -#endif diff --git a/RandBLAS/skge.hh b/RandBLAS/skge.hh index 647e02dd..61590105 100644 --- a/RandBLAS/skge.hh +++ b/RandBLAS/skge.hh @@ -26,9 +26,7 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // - -#ifndef randblas_skge_hh -#define randblas_skge_hh +#pragma once #include "RandBLAS/base.hh" #include "RandBLAS/exceptions.hh" @@ -1231,6 +1229,3 @@ inline void sketch_general( }; } // end namespace RandBLAS - - -#endif diff --git a/RandBLAS/sksy.hh b/RandBLAS/sksy.hh index 19aa232d..63c0954d 100644 --- a/RandBLAS/sksy.hh +++ b/RandBLAS/sksy.hh @@ -27,8 +27,7 @@ // POSSIBILITY OF SUCH DAMAGE. // -#ifndef randblas_sksy_hh -#define randblas_sksy_hh +#pragma once #include "RandBLAS/util.hh" #include "RandBLAS/base.hh" @@ -538,4 +537,3 @@ inline void sketch_symmetric( } } // end namespace RandBLAS -#endif diff --git a/RandBLAS/skve.hh b/RandBLAS/skve.hh index da473502..9657c23f 100644 --- a/RandBLAS/skve.hh +++ b/RandBLAS/skve.hh @@ -26,9 +26,7 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // - -#ifndef randblas_skve_hh -#define randblas_skve_hh +#pragma once #include "RandBLAS/base.hh" #include "RandBLAS/exceptions.hh" @@ -260,4 +258,3 @@ inline void sketch_vector( } } // end namespace RandBLAS -#endif diff --git a/RandBLAS/sparse_data/base.hh b/RandBLAS/sparse_data/base.hh index ac3c1ddd..fa9abafe 100644 --- a/RandBLAS/sparse_data/base.hh +++ b/RandBLAS/sparse_data/base.hh @@ -26,9 +26,7 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // - -#ifndef randblas_sparse_data_hh -#define randblas_sparse_data_hh +#pragma once #include "RandBLAS/config.h" #include "RandBLAS/base.hh" @@ -193,7 +191,3 @@ namespace RandBLAS { using RandBLAS::sparse_data::IndexBase; using RandBLAS::sparse_data::SparseMatrix; } - - - -#endif diff --git a/RandBLAS/sparse_data/conversions.hh b/RandBLAS/sparse_data/conversions.hh index 497053ab..1db914b6 100644 --- a/RandBLAS/sparse_data/conversions.hh +++ b/RandBLAS/sparse_data/conversions.hh @@ -26,9 +26,8 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // +#pragma once -#ifndef randblas_sparse_data_conversions -#define randblas_sparse_data_conversions #include "RandBLAS/base.hh" #include "RandBLAS/exceptions.hh" #include "RandBLAS/sparse_data/base.hh" @@ -209,5 +208,3 @@ void reindex_inplace(COOMatrix &A, IndexBase desired) { } } // end namespace RandBLAS::sparse_data::conversions - -#endif diff --git a/RandBLAS/sparse_data/coo_matrix.hh b/RandBLAS/sparse_data/coo_matrix.hh index 962f93f4..76f587f6 100644 --- a/RandBLAS/sparse_data/coo_matrix.hh +++ b/RandBLAS/sparse_data/coo_matrix.hh @@ -27,8 +27,8 @@ // POSSIBILITY OF SUCH DAMAGE. // -#ifndef randblas_sparse_data_coo -#define randblas_sparse_data_coo +#pragma once + #include "RandBLAS/base.hh" #include "RandBLAS/exceptions.hh" #include "RandBLAS/sparse_data/base.hh" @@ -409,4 +409,3 @@ void coo_to_dense(const COOMatrix &spmat, Layout layout, T *mat) { } // end namespace RandBLAS::sparse_data::coo -#endif diff --git a/RandBLAS/sparse_data/coo_spmm_impl.hh b/RandBLAS/sparse_data/coo_spmm_impl.hh index 60f1d397..2f798eae 100644 --- a/RandBLAS/sparse_data/coo_spmm_impl.hh +++ b/RandBLAS/sparse_data/coo_spmm_impl.hh @@ -27,8 +27,8 @@ // POSSIBILITY OF SUCH DAMAGE. // -#ifndef randblas_sparse_data_coo_multiply -#define randblas_sparse_data_coo_multiply +#pragma once + #include "RandBLAS/base.hh" #include "RandBLAS/exceptions.hh" #include "RandBLAS/sparse_data/base.hh" @@ -163,5 +163,3 @@ static void apply_coo_left_jki_p11( } // end namespace - -#endif diff --git a/RandBLAS/sparse_data/csc_matrix.hh b/RandBLAS/sparse_data/csc_matrix.hh index c296666b..eb0fa6bd 100644 --- a/RandBLAS/sparse_data/csc_matrix.hh +++ b/RandBLAS/sparse_data/csc_matrix.hh @@ -27,8 +27,8 @@ // POSSIBILITY OF SUCH DAMAGE. // -#ifndef randblas_sparse_data_csc -#define randblas_sparse_data_csc +#pragma once + #include "RandBLAS/base.hh" #include "RandBLAS/exceptions.hh" #include "RandBLAS/sparse_data/base.hh" @@ -246,7 +246,4 @@ void dense_to_csc(Layout layout, T* mat, T abs_tol, CSCMatrix &spmat) { return; } - -} - -#endif \ No newline at end of file +} // end namespace RandBLAS::sparse_data::csc diff --git a/RandBLAS/sparse_data/csc_spmm_impl.hh b/RandBLAS/sparse_data/csc_spmm_impl.hh index 8932e38a..77d63654 100644 --- a/RandBLAS/sparse_data/csc_spmm_impl.hh +++ b/RandBLAS/sparse_data/csc_spmm_impl.hh @@ -27,8 +27,7 @@ // POSSIBILITY OF SUCH DAMAGE. // -#ifndef randblas_sparse_data_csc_multiply -#define randblas_sparse_data_csc_multiply +#pragma once #include "RandBLAS/base.hh" #include "RandBLAS/exceptions.hh" #include "RandBLAS/sparse_data/base.hh" @@ -213,5 +212,4 @@ static void apply_csc_left_kib_rowmajor_1p1( return; } -} -#endif +} // end namespace RandBLAS::sparse_data::csc diff --git a/RandBLAS/sparse_data/csr_matrix.hh b/RandBLAS/sparse_data/csr_matrix.hh index 6844137b..9335b3b0 100644 --- a/RandBLAS/sparse_data/csr_matrix.hh +++ b/RandBLAS/sparse_data/csr_matrix.hh @@ -27,8 +27,8 @@ // POSSIBILITY OF SUCH DAMAGE. // -#ifndef randblas_sparse_data_csr -#define randblas_sparse_data_csr +#pragma once + #include "RandBLAS/base.hh" #include "RandBLAS/exceptions.hh" #include "RandBLAS/sparse_data/base.hh" @@ -262,4 +262,3 @@ void dense_to_csr(Layout layout, T* mat, T abs_tol, CSRMatrix &spmat) { } // end namespace RandBLAS::sparse_data::csr -#endif diff --git a/RandBLAS/sparse_data/csr_spmm_impl.hh b/RandBLAS/sparse_data/csr_spmm_impl.hh index d752064b..1665c6ec 100644 --- a/RandBLAS/sparse_data/csr_spmm_impl.hh +++ b/RandBLAS/sparse_data/csr_spmm_impl.hh @@ -27,8 +27,8 @@ // POSSIBILITY OF SUCH DAMAGE. // -#ifndef randblas_sparse_data_csr_multiply -#define randblas_sparse_data_csr_multiply +#pragma once + #include "RandBLAS/base.hh" #include "RandBLAS/exceptions.hh" #include "RandBLAS/sparse_data/base.hh" @@ -156,5 +156,3 @@ static void apply_csr_left_ikb_rowmajor( } } // end namespace RandBLAS::sparse_data::csr - -#endif diff --git a/RandBLAS/sparse_data/sksp.hh b/RandBLAS/sparse_data/sksp.hh index 02085c7a..6be7912b 100644 --- a/RandBLAS/sparse_data/sksp.hh +++ b/RandBLAS/sparse_data/sksp.hh @@ -27,8 +27,7 @@ // POSSIBILITY OF SUCH DAMAGE. // -#ifndef randblas_sksp_hh -#define randblas_sksp_hh +#pragma once #include "RandBLAS/base.hh" #include "RandBLAS/dense_skops.hh" @@ -625,6 +624,3 @@ inline void sketch_sparse( } } // end namespace RandBLAS - - -#endif diff --git a/RandBLAS/sparse_data/spmm_dispatch.hh b/RandBLAS/sparse_data/spmm_dispatch.hh index 4ea17544..ef6aa826 100644 --- a/RandBLAS/sparse_data/spmm_dispatch.hh +++ b/RandBLAS/sparse_data/spmm_dispatch.hh @@ -27,8 +27,8 @@ // POSSIBILITY OF SUCH DAMAGE. // -#ifndef randblas_sparse_data_spmm_dispatch -#define randblas_sparse_data_spmm_dispatch +#pragma once + #include "RandBLAS/base.hh" #include "RandBLAS/exceptions.hh" #include "RandBLAS/sparse_data/base.hh" @@ -384,5 +384,3 @@ inline void spmm(blas::Layout layout, blas::Op opA, blas::Op opB, int64_t m, int } } - -#endif diff --git a/RandBLAS/sparse_skops.hh b/RandBLAS/sparse_skops.hh index 770d5076..fe7c8aab 100644 --- a/RandBLAS/sparse_skops.hh +++ b/RandBLAS/sparse_skops.hh @@ -26,9 +26,7 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // - -#ifndef randblas_sparse_skops_hh -#define randblas_sparse_skops_hh +#pragma once #include "RandBLAS/config.h" #include "RandBLAS/base.hh" @@ -483,5 +481,3 @@ static auto transpose(SKOP const &S) { } } // end namespace RandBLAS::sparse - -#endif diff --git a/RandBLAS/util.hh b/RandBLAS/util.hh index b05c51ed..81babb0b 100644 --- a/RandBLAS/util.hh +++ b/RandBLAS/util.hh @@ -27,8 +27,7 @@ // POSSIBILITY OF SUCH DAMAGE. // -#ifndef randblas_util_hh -#define randblas_util_hh +#pragma once #include #include @@ -243,6 +242,76 @@ void flip_layout(blas::Layout layout_in, int64_t m, int64_t n, std::vector &A return; } -} // end namespace RandBLAS::util -#endif +template +void weights_to_cdf(T* p, int64_t len_p) { + T sum = 0.0; + for (int64_t i = 0; i < len_p; ++i) { + sum += p[i]; + p[i] = sum; + } + blas::scal(len_p, ((T)1.0)/sum, p, 1); +} + +/*** + * Assume cdf is a buffer specifying a cumulative probability distribution function. + * TF is a template parameter for a real floating point type. + * + * This function produces "num_samples" from the distribution specified by "cdf" + * and stores them in "samples". + */ +template +RNGState sample_indices_iid( + TF* cdf, int64_t len_cdf, int64_t* samples , int64_t num_samples, RandBLAS::RNGState state +) { + auto [ctr, key] = state; + RNG gen; + auto rv_array = r123ext::uneg11::generate(gen, ctr, key); + int64_t len_c = (int64_t) state.len_c; + int64_t rv_index = 0; + for (int64_t i = 0; i < num_samples; ++i) { + if ((i+1) % len_c == 1) { + ctr.incr(1); + rv_array = r123ext::uneg11::generate(gen, ctr, key); + rv_index = 0; + } + TF random_unif01 = ((TF) (rv_array[rv_index] + 1.0)) / ((TF) 2.0); + int64_t sample_index = std::lower_bound(cdf, cdf + len_cdf, random_unif01) - cdf; + // ^ uses binary search to set sample_index to the smallest value for which + // random_unif01 < cdf[sample_index]. + samples[i] = sample_index; + rv_index += 1; + } + return RNGState(ctr, key); +} + +/*** + * This function produces "num_samples" from the uniform distribution over + * {0, ..., max_index_exclusive - 1} and stores them in "samples". + */ +template +RNGState sample_indices_iid_uniform( + int64_t max_index_exclusive, int64_t* samples , int64_t num_samples, RandBLAS::RNGState state +) { + auto [ctr, key] = state; + RNG gen; + auto rv_array = r123ext::uneg11::generate(gen, ctr, key); + int64_t len_c = (int64_t) state.len_c; + int64_t rv_index = 0; + double dmie = (double) max_index_exclusive; + for (int64_t i = 0; i < num_samples; ++i) { + if ((i+1) % len_c == 1) { + ctr.incr(1); + rv_array = r123ext::uneg11::generate(gen, ctr, key); + rv_index = 0; + } + double random_unif01 = (double) (rv_array[rv_index] + 1.0) / 2.0; + int64_t sample_index = (int64_t) dmie * random_unif01; + samples[i] = sample_index; + rv_index += 1; + } + return RNGState(ctr, key); +} + + +} // end namespace RandBLAS::util From c1c917b64ac4996d0cd13480f06c2ba6a3a9a598 Mon Sep 17 00:00:00 2001 From: Riley Murray Date: Wed, 3 Jul 2024 16:10:53 -0400 Subject: [PATCH 02/15] improve tests for thread invariance of generating dense sketching operators --- test/test_datastructures/test_denseskop.cc | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/test/test_datastructures/test_denseskop.cc b/test/test_datastructures/test_denseskop.cc index a17e3ee6..c7ae4544 100644 --- a/test/test_datastructures/test_denseskop.cc +++ b/test/test_datastructures/test_denseskop.cc @@ -304,9 +304,7 @@ TEST_F(TestSubmatGeneration, diag) #if defined(RandBLAS_HAS_OpenMP) template -void DenseThreadTest() { - int64_t m = 32; - int64_t n = 8; +void DenseThreadTest(int64_t m, int64_t n) { int64_t d = m*n; std::vector base(d); @@ -319,10 +317,9 @@ void DenseThreadTest() { std::cerr << "with 1 thread: " << base << std::endl; // run with different numbers of threads, and check that the result is the same - int n_hyper = std::thread::hardware_concurrency(); - int n_threads = std::max(n_hyper / 2, 3); - + int n_threads = std::thread::hardware_concurrency(); for (int i = 2; i <= n_threads; ++i) { + std::fill(test.begin(), test.end(), (T) 0.0); omp_set_num_threads(i); RandBLAS::dense::fill_dense_submat_impl(n, test.data(), m, n, 0, state); std::cerr << "with " << i << " threads: " << test << std::endl; @@ -333,11 +330,19 @@ void DenseThreadTest() { } TEST(TestDenseThreading, UniformPhilox) { - DenseThreadTest(); + for (int i = 0; i < 10; ++i) { + DenseThreadTest(32, 8); + DenseThreadTest(1, 5); + DenseThreadTest(5, 1); + } } TEST(TestDenseThreading, GaussianPhilox) { - DenseThreadTest(); + for (int i = 0; i < 10; ++i) { + DenseThreadTest(32, 8); + DenseThreadTest(1, 5); + DenseThreadTest(5, 1); + } } #endif From 0083cf9bf0fc0162fe60f9b140b59b2f099ed13a Mon Sep 17 00:00:00 2001 From: Riley Murray Date: Wed, 3 Jul 2024 16:11:45 -0400 Subject: [PATCH 03/15] replace more include guards --- test/test_datastructures/test_spmats/common.hh | 7 ++----- test/test_matmul_cores/linop_common.hh | 6 ++---- test/test_matmul_cores/test_spmm/spmm_test_helpers.hh | 2 ++ 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/test/test_datastructures/test_spmats/common.hh b/test/test_datastructures/test_spmats/common.hh index 173304c7..8a7ffa37 100644 --- a/test/test_datastructures/test_spmats/common.hh +++ b/test/test_datastructures/test_spmats/common.hh @@ -27,8 +27,8 @@ // POSSIBILITY OF SUCH DAMAGE. // -#ifndef randblas_test_sparse_data_common_hh -#define randblas_test_sparse_data_common_hh +#pragma once + #include "RandBLAS/config.h" #include "RandBLAS/base.hh" #include "RandBLAS/dense_skops.hh" @@ -129,7 +129,4 @@ void coo_from_diag( return; } - } - -#endif diff --git a/test/test_matmul_cores/linop_common.hh b/test/test_matmul_cores/linop_common.hh index 0383f63d..e07c1888 100644 --- a/test/test_matmul_cores/linop_common.hh +++ b/test/test_matmul_cores/linop_common.hh @@ -27,8 +27,8 @@ // POSSIBILITY OF SUCH DAMAGE. // -#ifndef randblas_test_linop_common_hh -#define randblas_test_linop_common_hh +#pragma once + #include "RandBLAS/config.h" #include "RandBLAS/base.hh" #include "RandBLAS/dense_skops.hh" @@ -713,5 +713,3 @@ void test_right_apply_to_transposed( } } // end namespace test::linop_common - -#endif \ No newline at end of file diff --git a/test/test_matmul_cores/test_spmm/spmm_test_helpers.hh b/test/test_matmul_cores/test_spmm/spmm_test_helpers.hh index 5190e256..c07f16a4 100644 --- a/test/test_matmul_cores/test_spmm/spmm_test_helpers.hh +++ b/test/test_matmul_cores/test_spmm/spmm_test_helpers.hh @@ -27,6 +27,8 @@ // POSSIBILITY OF SUCH DAMAGE. // +#pragma once + #include "test/test_datastructures/test_spmats/common.hh" #include "test/test_matmul_cores/linop_common.hh" #include From 544d7133cc7fb549254b5db3b22b42eefdf8aff2 Mon Sep 17 00:00:00 2001 From: Riley Murray Date: Wed, 3 Jul 2024 16:13:32 -0400 Subject: [PATCH 04/15] babys first statistical tests --- test/CMakeLists.txt | 37 ++-- test/comparison.hh | 5 +- test/test_basic_rng/rng_common.hh | 186 +++++++++++++++++++++ test/test_basic_rng/test_sample_indices.cc | 163 ++++++++++++++++++ 4 files changed, 377 insertions(+), 14 deletions(-) create mode 100644 test/test_basic_rng/rng_common.hh create mode 100644 test/test_basic_rng/test_sample_indices.cc diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 822c1701..dfe6f94b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -5,6 +5,12 @@ set(tmp FALSE) if (GTest_FOUND) set(tmp TRUE) + ##################################################################### + # + # Tests with dense data and wrappers + # + ##################################################################### + add_executable(RandBLAS_tests comparison.hh test_datastructures/test_denseskop.cc @@ -19,13 +25,15 @@ if (GTest_FOUND) test_matmul_wrappers/test_sketch_vector.cc test_matmul_wrappers/test_sketch_symmetric.cc ) - target_link_libraries(RandBLAS_tests - RandBLAS - GTest::GTest - GTest::Main - ) + target_link_libraries(RandBLAS_tests RandBLAS GTest::GTest GTest::Main) gtest_discover_tests(RandBLAS_tests) + ##################################################################### + # + # Tests with sparse data + # + ##################################################################### + add_executable(SparseRandBLAS_tests comparison.hh @@ -40,13 +48,22 @@ if (GTest_FOUND) test_matmul_cores/test_spmm/test_spmm_csr.cc test_matmul_cores/test_spmm/test_spmm_coo.cc ) - target_link_libraries(SparseRandBLAS_tests - RandBLAS - GTest::GTest - GTest::Main - ) + target_link_libraries(SparseRandBLAS_tests RandBLAS GTest::GTest GTest::Main) gtest_discover_tests(SparseRandBLAS_tests) + ##################################################################### + # + # Statistical tests + # + ##################################################################### + + add_executable(RandBLAS_stats + test_basic_rng/rng_common.hh + test_basic_rng/test_sample_indices.cc + ) + target_link_libraries(RandBLAS_stats RandBLAS GTest::GTest GTest::Main) + gtest_discover_tests(RandBLAS_stats) + endif() message(STATUS "Checking for regression tests ... ${tmp}") diff --git a/test/comparison.hh b/test/comparison.hh index c8f69b27..15efd9f1 100644 --- a/test/comparison.hh +++ b/test/comparison.hh @@ -27,8 +27,7 @@ // POSSIBILITY OF SUCH DAMAGE. // -#ifndef randblas_test_comparisons_hh -#define randblas_test_comparisons_hh +#pragma once #include "RandBLAS.hh" #include @@ -239,5 +238,3 @@ void matrices_approx_equal( } } // end namespace test::comparison - -#endif diff --git a/test/test_basic_rng/rng_common.hh b/test/test_basic_rng/rng_common.hh new file mode 100644 index 00000000..fc66a748 --- /dev/null +++ b/test/test_basic_rng/rng_common.hh @@ -0,0 +1,186 @@ +#pragma once + +#include "RandBLAS.hh" +#include + +namespace RandBLAS_StatTests { + +// +// MARK: constants +// ^ and functions to perform lookups for the constants we store. + +namespace KolmogorovSmirnovConstants { + + +/*** From scipy.stats: critical_value = kstwo.ppf(1-significance, sample_size) */ + +const int SMALLEST_SAMPLE = 8; +const int LARGEST_SAMPLE = 16777216; + +const std::vector sample_sizes { + 8, 16, 32, 64, 128, 256, + 512, 1024, 2048, 4096, 8192, 16384, + 32768, 65536, 131072, 262144, 524288, 1048576, + 2097152, 4194304, 8388608, 16777216 +}; + +const double WEAKEST_SIGNIFICANCE = 0.05; +const double STRONGEST_SIGNIFICANCE = 1e-6; + +const std::vector significance_levels { + 0.05, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6 +}; + +const std::vector> critical_values {{ + // significance of 0.05 + 4.54266591e-01, 3.27333470e-01, 2.34240860e-01, 1.66933746e-01, + 1.18658276e-01, 8.42018587e-02, 5.96844982e-02, 4.22742678e-02, + 2.99273847e-02, 2.11791555e-02, 1.49845091e-02, 1.05999173e-02, + 7.49739992e-03, 5.30252270e-03, 3.74997893e-03, 2.65189975e-03, + 1.87530828e-03, 1.32610915e-03, 9.37733728e-04, 6.63094351e-04, + 4.68886747e-04, 3.31557115e-04 +}, { + // significance of 1e-2 + 5.41792524e-01, 3.92007307e-01, 2.80935776e-01, 2.00288899e-01, + 1.42362543e-01, 1.01005285e-01, 7.15810977e-02, 5.06916722e-02, + 3.58812433e-02, 2.53898259e-02, 1.79621350e-02, 1.27054989e-02, + 8.98630003e-03, 6.35534434e-03, 4.49443988e-03, 3.17831443e-03, + 2.24754012e-03, 1.58931697e-03, 1.12384982e-03, 7.94698321e-04, + 5.61944814e-04, 3.97359107e-04 +}, { + // significance of 1e-3 + 6.40978605e-01, 4.67504918e-01, 3.36105510e-01, 2.39914323e-01, + 1.70596759e-01, 1.21045341e-01, 8.57783224e-02, 6.07400729e-02, + 4.29898739e-02, 3.04175670e-02, 2.15177021e-02, 1.52198122e-02, + 1.07642402e-02, 7.61255632e-03, 5.38342952e-03, 3.80692734e-03, + 2.69203739e-03, 1.90362429e-03, 1.34609876e-03, 9.51852090e-04, + 6.73069322e-04, 4.75936005e-04 +}, { + // significance of 1e-4 + 7.20107998e-01, 5.30358433e-01, 3.82763541e-01, 2.73655631e-01, + 1.94715231e-01, 1.38189709e-01, 9.79338402e-02, 6.93467436e-02, + 4.90797287e-02, 3.47251628e-02, 2.45641333e-02, 1.73741413e-02, + 1.22876435e-02, 8.68978729e-03, 6.14515467e-03, 4.34555112e-03, + 3.07290290e-03, 2.17293722e-03, 1.53653188e-03, 1.08650868e-03, + 7.68285928e-04, 5.43264318e-04 +}, { + // significance of 1e-5 + 7.84314235e-01, 5.84534035e-01, 4.23688590e-01, 3.03463697e-01, + 2.16091906e-01, 1.53407046e-01, 1.08732306e-01, 7.69956230e-02, + 5.44929246e-02, 3.85544959e-02, 2.72724540e-02, 1.92894157e-02, + 1.36420186e-02, 9.64750036e-03, 6.82236902e-03, 4.82441714e-03, + 3.41151342e-03, 2.41237141e-03, 1.70583756e-03, 1.20622593e-03, + 8.52938821e-04, 6.03122960e-0 +}, { + // significance of 1e-6 + 8.36962528e-01, 6.32173765e-01, 4.60387149e-01, 3.30395198e-01, + 2.35470356e-01, 1.67220735e-01, 1.18543880e-01, 8.39483725e-02, + 5.94144379e-02, 4.20363448e-02, 2.97351313e-02, 2.10310168e-02, + 1.48735960e-02, 1.05183852e-02, 7.43818754e-03, 5.25987011e-03, + 3.71942641e-03, 2.63009921e-03, 1.85979452e-03, 1.31508999e-03, + 9.29917360e-04, 6.57555013e-04 +}}; + +/*** + * Returns the index in significance_levels for the "least significant" value + * that is "more significant" than "sig". + * + * The correctness of this function depends on significance_levels being sorted + * in decreasing order (which corresponds to weakest to strongest significances). + */ +int significance_rep(double sig) { + randblas_require(STRONGEST_SIGNIFICANCE <= sig && sig <= WEAKEST_SIGNIFICANCE); + int num_siglevels = (int) significance_levels.size(); + for (int i = 0; i < num_siglevels; ++i) { + if (significance_levels[i] <= sig) + return i; + } + // This code shouldn't be reachable! + randblas_require(false); + return -1; +} + +/*** + * Returns the index in sample_sizes for the smallest sample size that's >= n. + * + * The correctness of this function depends on sample_sizes being sorted in + * increasing order. + */ +int sample_size_rep(int n) { + randblas_require(SMALLEST_SAMPLE <= n && n <= LARGEST_SAMPLE); + int num_sample_sizes = (int) sample_sizes.size(); + for (int i = 0; i < num_sample_sizes; ++i) { + if (sample_sizes[i] <= n) + return i; + } + // This code shouldn't be reachable! + randblas_require(false); + return -1; +} + +std::tuple critical_value_rep(int n, double sig) { + int i = significance_rep(sig); + auto override_sig = significance_levels[i]; + int j = sample_size_rep(n); + auto override_sample = sample_sizes[j]; + auto cv = critical_values[i][j]; + return {cv, override_sample, override_sig}; +} + +} + +// +// MARK: combinatorics +// + +double log_binomial_coefficient(int64_t n, int64_t k) { + double result = 0.0; + for (int64_t i = 1; i <= k; ++i) { + result += std::log(static_cast(n - i + 1)) - std::log(static_cast(i)); + } + return result; +} + + +// +// MARK: hypergeometric +// + +/*** + * Compute the probability mass function of the hypergeometric distribution with parameters N, K, D. + * Concretely ... + * + * Suppose we draw D items without replacement from a set of size N that has K distinguished elements. + * This function returns the probability that the sample of D items will contain observed_k elements + * from the distinguished set. + */ +double hypergeometric_pmf(int64_t N, int64_t K, int64_t D, int64_t observed_k) { + randblas_require(0 <= K && K <= N); + randblas_require(0 <= D && D <= N); + randblas_require(0 <= observed_k && observed_k <= K); + double lognum = log_binomial_coefficient(N - K, D - observed_k) + log_binomial_coefficient(K, observed_k); + double logden = log_binomial_coefficient(N, D); + double exparg = lognum - logden; + double out = std::exp(exparg); + return out; +} + +double hypergeometric_mean(int64_t N, int64_t K, int64_t D) { + double dN = (double) N; + double dK = (double) K; + double dD = (double) D; + return dD * dK / dN; +} + +double hypergeometric_variance(int64_t N, int64_t K, int64_t D) { + double dN = (double) N; + double dK = (double) K; + double dD = (double) D; + + auto t1 = dK / dN; + auto t2 = (dN - dK) / dN; + auto t3 = (dN - dD) / (dN - 1.0); + return dD * t1 * t2 * t3; +} + +} // end namespace RandBLAS_StatTests diff --git a/test/test_basic_rng/test_sample_indices.cc b/test/test_basic_rng/test_sample_indices.cc new file mode 100644 index 00000000..fe6a80fc --- /dev/null +++ b/test/test_basic_rng/test_sample_indices.cc @@ -0,0 +1,163 @@ +// Copyright, 2024. See LICENSE for copyright holder information. +// +// 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 copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR 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. +// + +#include "RandBLAS/config.h" +#include "RandBLAS/base.hh" +#include "RandBLAS/util.hh" +using RandBLAS::RNGState; +#include "rng_common.hh" + +#include +#include +#include +#include +#include +#include +#include + + +class TestSampleIndices : public ::testing::Test +{ + protected: + + virtual void SetUp(){}; + + virtual void TearDown(){}; + + static void test_iid_uniform_smoke(int64_t N, int64_t k, uint32_t seed) { + RNGState state(seed); + std::vector samples(k, -1); + RandBLAS::util::sample_indices_iid_uniform(N, samples.data(), k, state); + int64_t* data = samples.data(); + for (int64_t i = 0; i < k; ++i) { + ASSERT_LT(data[i], N); + ASSERT_GE(data[i], 0); + } + return; + } + + static void test_iid_uniform_kolmogorov_smirnov(int64_t N, double significance, int64_t num_samples, uint32_t seed) { + randblas_require(N <= (int64_t) 1e6); + + using RandBLAS_StatTests::KolmogorovSmirnovConstants::critical_value_rep; + auto [critical_value, override_ns, override_sig] = critical_value_rep(num_samples, significance); + significance = (double) override_sig; + num_samples = (int64_t) override_ns; + + RNGState state(seed); + std::vector samples(num_samples, -1); + RandBLAS::util::sample_indices_iid_uniform(N, samples.data(), num_samples, state); + std::vector sample_cdf(N, 0.0); + for (int64_t s : samples) + sample_cdf[s] += 1; + RandBLAS::util::weights_to_cdf(sample_cdf.data(), N); + + std::vector true_cdf(N, 1.0); + RandBLAS::util::weights_to_cdf(true_cdf.data(), N); + + + for (int i = 0; i < num_samples; ++i) { + float diff = std::abs(sample_cdf[i] - true_cdf[i]); + ASSERT_LT(diff, critical_value); + } + return; + } + +}; + + +TEST_F(TestSampleIndices, smoke_3_x_10) { + for (uint32_t i = 0; i < 10; ++i) + test_iid_uniform_smoke(3, 10, i); +} + +TEST_F(TestSampleIndices, smoke_10_x_3) { + for (uint32_t i = 0; i < 10; ++i) + test_iid_uniform_smoke(10, 3, i); +} + +TEST_F(TestSampleIndices, smoke_med) { + for (uint32_t i = 0; i < 10; ++i) + test_iid_uniform_smoke((int) 1e6 , 6000, i); +} + +TEST_F(TestSampleIndices, smoke_big) { + int64_t huge_N = std::numeric_limits::max() / 2; + for (uint32_t i = 0; i < 10; ++i) + test_iid_uniform_smoke(huge_N, 1000, i); +} + +TEST_F(TestSampleIndices, iid_uniform_ks_generous) { + double s = 1e-6; + test_iid_uniform_kolmogorov_smirnov(100, s, 100000, 0); + test_iid_uniform_kolmogorov_smirnov(10000, s, 1000, 0); + test_iid_uniform_kolmogorov_smirnov(1000000, s, 1000, 0); +} + +TEST_F(TestSampleIndices, iid_uniform_ks_moderate) { + float s = 1e-4; + test_iid_uniform_kolmogorov_smirnov(100, s, 100000, 0); + test_iid_uniform_kolmogorov_smirnov(10000, s, 1000, 0); + test_iid_uniform_kolmogorov_smirnov(1000000, s, 1000, 0); +} + +TEST_F(TestSampleIndices, iid_uniform_ks_skeptical) { + float s = 1e-2; + test_iid_uniform_kolmogorov_smirnov(100, s, 100000, 0); + test_iid_uniform_kolmogorov_smirnov(10000, s, 1000, 0); + test_iid_uniform_kolmogorov_smirnov(1000000, s, 1000, 0); +} + + + +// class TestSampleIndices : public ::testing::Test +// { +// protected: + +// virtual void SetUp(){}; + +// virtual void TearDown(){}; + +// template +// static void test_basic( + +// ) { +// return; +// } + +// }; + + +// TEST_F(TestSampleIndices, smoke) +// { +// // do something +// } + + + From 4bfe1e03828d59a25c4cb6a269dfd63d3b9b3e5a Mon Sep 17 00:00:00 2001 From: Riley Murray Date: Wed, 3 Jul 2024 16:40:21 -0400 Subject: [PATCH 05/15] tests for sampling from nonuniform distributions --- test/test_basic_rng/rng_common.hh | 10 +++ test/test_basic_rng/test_sample_indices.cc | 73 +++++++++++++++++----- 2 files changed, 69 insertions(+), 14 deletions(-) diff --git a/test/test_basic_rng/rng_common.hh b/test/test_basic_rng/rng_common.hh index fc66a748..af5b1d32 100644 --- a/test/test_basic_rng/rng_common.hh +++ b/test/test_basic_rng/rng_common.hh @@ -127,6 +127,16 @@ std::tuple critical_value_rep(int n, double sig) { return {cv, override_sample, override_sig}; } +template +double critical_value_rep_mutator(TI &n, double &sig) { + int i = significance_rep(sig); + sig = significance_levels[i]; + int j = sample_size_rep(n); + n = (TI) sample_sizes[j]; + double cv = critical_values[i][j]; + return cv; +} + } // diff --git a/test/test_basic_rng/test_sample_indices.cc b/test/test_basic_rng/test_sample_indices.cc index fe6a80fc..aaea689a 100644 --- a/test/test_basic_rng/test_sample_indices.cc +++ b/test/test_basic_rng/test_sample_indices.cc @@ -62,30 +62,53 @@ class TestSampleIndices : public ::testing::Test return; } + static void index_set_kolmogorov_smirnov_tester( + std::vector &samples, std::vector &true_cdf, double critical_value + ) { + auto num_samples = (int) samples.size(); + auto N = (int64_t) true_cdf.size(); + std::vector sample_cdf(N, 0.0); + for (int64_t s : samples) + sample_cdf[s] += 1; + RandBLAS::util::weights_to_cdf(sample_cdf.data(), N); + + for (int i = 0; i < num_samples; ++i) { + auto diff = (double) std::abs(sample_cdf[i] - true_cdf[i]); + ASSERT_LT(diff, critical_value); + } + return; + } + static void test_iid_uniform_kolmogorov_smirnov(int64_t N, double significance, int64_t num_samples, uint32_t seed) { - randblas_require(N <= (int64_t) 1e6); + using RandBLAS_StatTests::KolmogorovSmirnovConstants::critical_value_rep_mutator; + auto critical_value = critical_value_rep_mutator(num_samples, significance); - using RandBLAS_StatTests::KolmogorovSmirnovConstants::critical_value_rep; - auto [critical_value, override_ns, override_sig] = critical_value_rep(num_samples, significance); - significance = (double) override_sig; - num_samples = (int64_t) override_ns; + std::vector true_cdf(N, 1.0); + RandBLAS::util::weights_to_cdf(true_cdf.data(), N); RNGState state(seed); std::vector samples(num_samples, -1); RandBLAS::util::sample_indices_iid_uniform(N, samples.data(), num_samples, state); - std::vector sample_cdf(N, 0.0); - for (int64_t s : samples) - sample_cdf[s] += 1; - RandBLAS::util::weights_to_cdf(sample_cdf.data(), N); - std::vector true_cdf(N, 1.0); + index_set_kolmogorov_smirnov_tester(samples, true_cdf, critical_value); + return; + } + + static void test_iid_kolmogorov_smirnov(int64_t N, double significance, int64_t num_samples, uint32_t seed) { + using RandBLAS_StatTests::KolmogorovSmirnovConstants::critical_value_rep_mutator; + auto critical_value = critical_value_rep_mutator(num_samples, significance); + + // Make the true CDF + std::vector true_cdf{}; + for (int i = 0; i < N; ++i) + true_cdf.push_back(1.0/((float)i + 1.0)); RandBLAS::util::weights_to_cdf(true_cdf.data(), N); + RNGState state(seed); + std::vector samples(num_samples, -1); + RandBLAS::util::sample_indices_iid(true_cdf.data(), N, samples.data(), num_samples, state); - for (int i = 0; i < num_samples; ++i) { - float diff = std::abs(sample_cdf[i] - true_cdf[i]); - ASSERT_LT(diff, critical_value); - } + index_set_kolmogorov_smirnov_tester(samples, true_cdf, critical_value); return; } @@ -135,6 +158,28 @@ TEST_F(TestSampleIndices, iid_uniform_ks_skeptical) { } +TEST_F(TestSampleIndices, iid_ks_generous) { + double s = 1e-6; + test_iid_kolmogorov_smirnov(100, s, 100000, 0); + test_iid_kolmogorov_smirnov(10000, s, 1000, 0); + test_iid_kolmogorov_smirnov(1000000, s, 1000, 0); +} + +TEST_F(TestSampleIndices, iid_ks_moderate) { + float s = 1e-4; + test_iid_kolmogorov_smirnov(100, s, 100000, 0); + test_iid_kolmogorov_smirnov(10000, s, 1000, 0); + test_iid_kolmogorov_smirnov(1000000, s, 1000, 0); +} + +TEST_F(TestSampleIndices, iid_ks_skeptical) { + float s = 1e-2; + test_iid_kolmogorov_smirnov(100, s, 100000, 0); + test_iid_kolmogorov_smirnov(10000, s, 1000, 0); + test_iid_kolmogorov_smirnov(1000000, s, 1000, 0); +} + + // class TestSampleIndices : public ::testing::Test // { From adab6c667ea7c13cb12bc05164a8e566ce0fdc03 Mon Sep 17 00:00:00 2001 From: Riley Murray Date: Wed, 3 Jul 2024 21:16:09 -0400 Subject: [PATCH 06/15] API tweaks, plus docs --- RandBLAS/base.hh | 13 +++++- RandBLAS/util.hh | 49 +++++++++++++--------- test/test_basic_rng/test_sample_indices.cc | 8 ++-- 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/RandBLAS/base.hh b/RandBLAS/base.hh index 62aa456a..a2382e80 100644 --- a/RandBLAS/base.hh +++ b/RandBLAS/base.hh @@ -53,9 +53,18 @@ /// code common across the project namespace RandBLAS { +/** + * Stores stride information for a matrix represented as a buffer. + * The intended semantics for a buffer "A" and the conceptualized + * matrix "mat(A)" are + * + * mat(A)[i, j] == A[i * inter_row_stride + j * inter_col_stride]. + * + * for all (i, j) within the bounds of mat(A). + */ struct stride_64t { - int64_t inter_row_stride; - int64_t inter_col_stride; + int64_t inter_row_stride; // step down a column + int64_t inter_col_stride; // step along a row }; inline stride_64t layout_to_strides(blas::Layout layout, int64_t ldim) { diff --git a/RandBLAS/util.hh b/RandBLAS/util.hh index 81babb0b..4dadf795 100644 --- a/RandBLAS/util.hh +++ b/RandBLAS/util.hh @@ -244,39 +244,50 @@ void flip_layout(blas::Layout layout_in, int64_t m, int64_t n, std::vector &A template -void weights_to_cdf(T* p, int64_t len_p) { +void weights_to_cdf(int64_t n, T* w, T error_if_below = -std::numeric_limits::epsilon()) { T sum = 0.0; - for (int64_t i = 0; i < len_p; ++i) { - sum += p[i]; - p[i] = sum; + for (int64_t i = 0; i < n; ++i) { + T val = w[i]; + randblas_require(val >= error_if_below); + val = std::max(val, (T) 0.0); + sum += val; + w[i] = sum; } - blas::scal(len_p, ((T)1.0)/sum, p, 1); + randblas_require(sum >= ((T) std::sqrt(n)) * std::numeric_limits::epsilon()); + blas::scal(n, ((T)1.0) / sum, w, 1); + return; +} + +template +static inline TO uneg11_to_uneg01(TI in) { + return ((TO) in + (TO) 1.0)/ ((TO) 2.0); } /*** - * Assume cdf is a buffer specifying a cumulative probability distribution function. + * cdf represents a cumulative distribution function over {0, ..., n - 1}. + * * TF is a template parameter for a real floating point type. * - * This function produces "num_samples" from the distribution specified by "cdf" - * and stores them in "samples". + * We overwrite the "samples" buffer with k (independent) samples from the + * distribution specified by cdf. */ template RNGState sample_indices_iid( - TF* cdf, int64_t len_cdf, int64_t* samples , int64_t num_samples, RandBLAS::RNGState state + int64_t n, TF* cdf, int64_t k, int64_t* samples, RandBLAS::RNGState state ) { auto [ctr, key] = state; RNG gen; auto rv_array = r123ext::uneg11::generate(gen, ctr, key); int64_t len_c = (int64_t) state.len_c; int64_t rv_index = 0; - for (int64_t i = 0; i < num_samples; ++i) { + for (int64_t i = 0; i < k; ++i) { if ((i+1) % len_c == 1) { ctr.incr(1); rv_array = r123ext::uneg11::generate(gen, ctr, key); rv_index = 0; } - TF random_unif01 = ((TF) (rv_array[rv_index] + 1.0)) / ((TF) 2.0); - int64_t sample_index = std::lower_bound(cdf, cdf + len_cdf, random_unif01) - cdf; + auto random_unif01 = uneg11_to_uneg01(rv_array[rv_index]); + int64_t sample_index = std::lower_bound(cdf, cdf + n, random_unif01) - cdf; // ^ uses binary search to set sample_index to the smallest value for which // random_unif01 < cdf[sample_index]. samples[i] = sample_index; @@ -286,27 +297,27 @@ RNGState sample_indices_iid( } /*** - * This function produces "num_samples" from the uniform distribution over - * {0, ..., max_index_exclusive - 1} and stores them in "samples". + * Overwrite the "samples" buffer with k (independent) samples from the + * uniform distribution over {0, ..., n - 1}. */ template RNGState sample_indices_iid_uniform( - int64_t max_index_exclusive, int64_t* samples , int64_t num_samples, RandBLAS::RNGState state + int64_t n, int64_t* samples , int64_t k, RandBLAS::RNGState state ) { auto [ctr, key] = state; RNG gen; auto rv_array = r123ext::uneg11::generate(gen, ctr, key); int64_t len_c = (int64_t) state.len_c; int64_t rv_index = 0; - double dmie = (double) max_index_exclusive; - for (int64_t i = 0; i < num_samples; ++i) { + double dN = (double) n; + for (int64_t i = 0; i < k; ++i) { if ((i+1) % len_c == 1) { ctr.incr(1); rv_array = r123ext::uneg11::generate(gen, ctr, key); rv_index = 0; } - double random_unif01 = (double) (rv_array[rv_index] + 1.0) / 2.0; - int64_t sample_index = (int64_t) dmie * random_unif01; + auto random_unif01 = uneg11_to_uneg01(rv_array[rv_index]); + int64_t sample_index = (int64_t) dN * random_unif01; samples[i] = sample_index; rv_index += 1; } diff --git a/test/test_basic_rng/test_sample_indices.cc b/test/test_basic_rng/test_sample_indices.cc index aaea689a..08557a8a 100644 --- a/test/test_basic_rng/test_sample_indices.cc +++ b/test/test_basic_rng/test_sample_indices.cc @@ -70,7 +70,7 @@ class TestSampleIndices : public ::testing::Test std::vector sample_cdf(N, 0.0); for (int64_t s : samples) sample_cdf[s] += 1; - RandBLAS::util::weights_to_cdf(sample_cdf.data(), N); + RandBLAS::util::weights_to_cdf(N, sample_cdf.data()); for (int i = 0; i < num_samples; ++i) { auto diff = (double) std::abs(sample_cdf[i] - true_cdf[i]); @@ -84,7 +84,7 @@ class TestSampleIndices : public ::testing::Test auto critical_value = critical_value_rep_mutator(num_samples, significance); std::vector true_cdf(N, 1.0); - RandBLAS::util::weights_to_cdf(true_cdf.data(), N); + RandBLAS::util::weights_to_cdf(N, true_cdf.data()); RNGState state(seed); std::vector samples(num_samples, -1); @@ -102,11 +102,11 @@ class TestSampleIndices : public ::testing::Test std::vector true_cdf{}; for (int i = 0; i < N; ++i) true_cdf.push_back(1.0/((float)i + 1.0)); - RandBLAS::util::weights_to_cdf(true_cdf.data(), N); + RandBLAS::util::weights_to_cdf(N, true_cdf.data()); RNGState state(seed); std::vector samples(num_samples, -1); - RandBLAS::util::sample_indices_iid(true_cdf.data(), N, samples.data(), num_samples, state); + RandBLAS::util::sample_indices_iid(N, true_cdf.data(), num_samples, samples.data(), state); index_set_kolmogorov_smirnov_tester(samples, true_cdf, critical_value); return; From a191110299866ebb668dee106b1f45387f2a74d2 Mon Sep 17 00:00:00 2001 From: Riley Murray Date: Thu, 4 Jul 2024 08:34:15 -0400 Subject: [PATCH 07/15] tests for degenerate distributions --- RandBLAS/util.hh | 2 - test/test_basic_rng/test_sample_indices.cc | 100 +++++++++++++-------- 2 files changed, 61 insertions(+), 41 deletions(-) diff --git a/RandBLAS/util.hh b/RandBLAS/util.hh index 4dadf795..177a1f9a 100644 --- a/RandBLAS/util.hh +++ b/RandBLAS/util.hh @@ -288,8 +288,6 @@ RNGState sample_indices_iid( } auto random_unif01 = uneg11_to_uneg01(rv_array[rv_index]); int64_t sample_index = std::lower_bound(cdf, cdf + n, random_unif01) - cdf; - // ^ uses binary search to set sample_index to the smallest value for which - // random_unif01 < cdf[sample_index]. samples[i] = sample_index; rv_index += 1; } diff --git a/test/test_basic_rng/test_sample_indices.cc b/test/test_basic_rng/test_sample_indices.cc index 08557a8a..5cf041f1 100644 --- a/test/test_basic_rng/test_sample_indices.cc +++ b/test/test_basic_rng/test_sample_indices.cc @@ -94,14 +94,14 @@ class TestSampleIndices : public ::testing::Test return; } - static void test_iid_kolmogorov_smirnov(int64_t N, double significance, int64_t num_samples, uint32_t seed) { + static void test_iid_kolmogorov_smirnov(int64_t N, int exponent, double significance, int64_t num_samples, uint32_t seed) { using RandBLAS_StatTests::KolmogorovSmirnovConstants::critical_value_rep_mutator; auto critical_value = critical_value_rep_mutator(num_samples, significance); // Make the true CDF std::vector true_cdf{}; for (int i = 0; i < N; ++i) - true_cdf.push_back(1.0/((float)i + 1.0)); + true_cdf.push_back(std::pow(1.0/((float)i + 1.0), exponent)); RandBLAS::util::weights_to_cdf(N, true_cdf.data()); RNGState state(seed); @@ -111,6 +111,42 @@ class TestSampleIndices : public ::testing::Test index_set_kolmogorov_smirnov_tester(samples, true_cdf, critical_value); return; } + + static void test_iid_degenerate_distributions(uint32_t seed) { + int64_t N = 100; + int64_t num_samples = N*N; + std::vector samples(num_samples, -1); + RNGState state(seed); + + using RandBLAS::util::weights_to_cdf; + using RandBLAS::util::sample_indices_iid; + + // Test case 1: distribution is nonuniform, with mass only on even elements != 10. + std::vector true_cdf(N, 0.0); + for (int i = 0; i < N; i = i + 2) + true_cdf[i] = 1.0f / ((float) i + 1.0f); + true_cdf[10] = 0.0; + weights_to_cdf(N, true_cdf.data()); + sample_indices_iid(N, true_cdf.data(), num_samples, samples.data(), state); + for (auto s : samples) { + ASSERT_FALSE(s == 10 || s % 2 == 1) << "s = " << s; + } + + // Test case 2: distribution is trivial (a delta function), + // and a negative weight needs to be clipped without error. + std::fill(true_cdf.begin(), true_cdf.end(), 0.0); + std::fill(samples.begin(), samples.end(), -1); + true_cdf[17] = 99.0f; + true_cdf[3] = -std::numeric_limits::epsilon()/10; + randblas_require(true_cdf[3] < 0); + weights_to_cdf(N, true_cdf.data()); + ASSERT_GE(true_cdf[17], 0.0f); + sample_indices_iid(N, true_cdf.data(), num_samples, samples.data(), state); + for (auto s : samples) { + ASSERT_EQ(s, 17); + } + return; + } }; @@ -136,6 +172,11 @@ TEST_F(TestSampleIndices, smoke_big) { test_iid_uniform_smoke(huge_N, 1000, i); } +TEST_F(TestSampleIndices, support_of_degenerate_distributions) { + for (uint32_t i = 789; i < 799; ++i) + test_iid_degenerate_distributions(i); +} + TEST_F(TestSampleIndices, iid_uniform_ks_generous) { double s = 1e-6; test_iid_uniform_kolmogorov_smirnov(100, s, 100000, 0); @@ -160,49 +201,30 @@ TEST_F(TestSampleIndices, iid_uniform_ks_skeptical) { TEST_F(TestSampleIndices, iid_ks_generous) { double s = 1e-6; - test_iid_kolmogorov_smirnov(100, s, 100000, 0); - test_iid_kolmogorov_smirnov(10000, s, 1000, 0); - test_iid_kolmogorov_smirnov(1000000, s, 1000, 0); + test_iid_kolmogorov_smirnov(100, 1, s, 100000, 0); + test_iid_kolmogorov_smirnov(10000, 1, s, 1000, 0); + test_iid_kolmogorov_smirnov(1000000, 1, s, 1000, 0); + test_iid_kolmogorov_smirnov(100, 3, s, 100000, 0); + test_iid_kolmogorov_smirnov(10000, 3, s, 1000, 0); + test_iid_kolmogorov_smirnov(1000000, 3, s, 1000, 0); } TEST_F(TestSampleIndices, iid_ks_moderate) { float s = 1e-4; - test_iid_kolmogorov_smirnov(100, s, 100000, 0); - test_iid_kolmogorov_smirnov(10000, s, 1000, 0); - test_iid_kolmogorov_smirnov(1000000, s, 1000, 0); + test_iid_kolmogorov_smirnov(100, 1, s, 100000, 0); + test_iid_kolmogorov_smirnov(10000, 1, s, 1000, 0); + test_iid_kolmogorov_smirnov(1000000, 1, s, 1000, 0); + test_iid_kolmogorov_smirnov(100, 3, s, 100000, 0); + test_iid_kolmogorov_smirnov(10000, 3, s, 1000, 0); + test_iid_kolmogorov_smirnov(1000000, 3, s, 1000, 0); } TEST_F(TestSampleIndices, iid_ks_skeptical) { float s = 1e-2; - test_iid_kolmogorov_smirnov(100, s, 100000, 0); - test_iid_kolmogorov_smirnov(10000, s, 1000, 0); - test_iid_kolmogorov_smirnov(1000000, s, 1000, 0); + test_iid_kolmogorov_smirnov(100, 1, s, 100000, 0); + test_iid_kolmogorov_smirnov(10000, 1, s, 1000, 0); + test_iid_kolmogorov_smirnov(1000000, 1, s, 1000, 0); + test_iid_kolmogorov_smirnov(100, 3, s, 100000, 0); + test_iid_kolmogorov_smirnov(10000, 3, s, 1000, 0); + test_iid_kolmogorov_smirnov(1000000, 3, s, 1000, 0); } - - - -// class TestSampleIndices : public ::testing::Test -// { -// protected: - -// virtual void SetUp(){}; - -// virtual void TearDown(){}; - -// template -// static void test_basic( - -// ) { -// return; -// } - -// }; - - -// TEST_F(TestSampleIndices, smoke) -// { -// // do something -// } - - - From 8ebaca640e43a2014ac319abe721227043596fa1 Mon Sep 17 00:00:00 2001 From: Riley Murray Date: Tue, 16 Jul 2024 16:26:13 -0400 Subject: [PATCH 08/15] remove unused (and kind of gross) utility function --- RandBLAS/util.hh | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/RandBLAS/util.hh b/RandBLAS/util.hh index 177a1f9a..c912b159 100644 --- a/RandBLAS/util.hh +++ b/RandBLAS/util.hh @@ -57,26 +57,6 @@ void safe_scal(int64_t n, T a, T* x, int64_t inc_x) { } } -template -void genmat( - int64_t n_rows, - int64_t n_cols, - T* mat, - uint64_t seed) -{ - typedef r123::Philox2x64 CBRNG; - CBRNG::key_type key = {{seed}}; - CBRNG::ctr_type ctr = {{0,0}}; - CBRNG g; - uint64_t prod = n_rows * n_cols; - for (uint64_t i = 0; i < prod; ++i) - { - ctr[0] = i; - CBRNG::ctr_type rand = g(ctr, key); - mat[i] = r123::uneg11(rand.v[0]); - } -} - template void print_colmaj(int64_t n_rows, int64_t n_cols, T *a, char label[]) { From e0f173e9a585ae20f7a1f92df5daafae2d52dc29 Mon Sep 17 00:00:00 2001 From: Riley Murray Date: Tue, 16 Jul 2024 17:20:45 -0400 Subject: [PATCH 09/15] try suggestion from Robot Friend to resolve compiler error with gcc --- test/test_basic_rng/rng_common.hh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/test_basic_rng/rng_common.hh b/test/test_basic_rng/rng_common.hh index af5b1d32..f1103b4b 100644 --- a/test/test_basic_rng/rng_common.hh +++ b/test/test_basic_rng/rng_common.hh @@ -2,6 +2,7 @@ #include "RandBLAS.hh" #include +#include namespace RandBLAS_StatTests { @@ -31,7 +32,7 @@ const std::vector significance_levels { 0.05, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6 }; -const std::vector> critical_values {{ +const std::array, 6> critical_values {{{ // significance of 0.05 4.54266591e-01, 3.27333470e-01, 2.34240860e-01, 1.66933746e-01, 1.18658276e-01, 8.42018587e-02, 5.96844982e-02, 4.22742678e-02, @@ -79,7 +80,7 @@ const std::vector> critical_values {{ 1.48735960e-02, 1.05183852e-02, 7.43818754e-03, 5.25987011e-03, 3.71942641e-03, 2.63009921e-03, 1.85979452e-03, 1.31508999e-03, 9.29917360e-04, 6.57555013e-04 -}}; +}}}; /*** * Returns the index in significance_levels for the "least significant" value From a2f800cdae35ec5fce9de56fa91909163a32bac9 Mon Sep 17 00:00:00 2001 From: Riley Murray Date: Thu, 25 Jul 2024 09:19:01 -0400 Subject: [PATCH 10/15] known-answer-tests for Random123 run and pass. Still cleaning up implementation to use Googletest. --- test/test_basic_rng/kat_vectors.txt | 75 ++++ test/test_basic_rng/r123_rngNxW.mm | 57 +++ test/test_basic_rng/test_r123_kat.cc | 624 +++++++++++++++++++++++++++ 3 files changed, 756 insertions(+) create mode 100644 test/test_basic_rng/kat_vectors.txt create mode 100644 test/test_basic_rng/r123_rngNxW.mm create mode 100644 test/test_basic_rng/test_r123_kat.cc diff --git a/test/test_basic_rng/kat_vectors.txt b/test/test_basic_rng/kat_vectors.txt new file mode 100644 index 00000000..21c453dc --- /dev/null +++ b/test/test_basic_rng/kat_vectors.txt @@ -0,0 +1,75 @@ +# This file is copied from https://github.com/DEShawResearch/random123/blob/main/tests/kat_vectors +# +# For each generator, we test: gen(0, 0), gen(fff, fff) and gen(ctr=digits_of_pi, key=more_digits_of_pi). +# Ignoring endianness, these are the first few hexdigits of pi: +# 243F6A88 85A308D3 13198A2E 03707344 A4093822 299F31D0 082EFA98 EC4E6C89 452821E6 38D01377 BE5466CF 34E90C6C C0AC29B7 C97C50DD 3F84D5B5 B5470917 9216D5D9 8979FB1BD +# +#nameNxW R CTR KEY EXPECTED +# +philox2x32 7 00000000 00000000 00000000 257a3673 cd26be2a +philox2x32 7 ffffffff ffffffff ffffffff ab302c4d 3dc9d239 +philox2x32 7 243f6a88 85a308d3 13198a2e bedbbe6b e4c770b3 +philox2x32 10 00000000 00000000 00000000 ff1dae59 6cd10df2 +philox2x32 10 ffffffff ffffffff ffffffff 2c3f628b ab4fd7ad +philox2x32 10 243f6a88 85a308d3 13198a2e dd7ce038 f62a4c12 +# +philox4x32 7 00000000 00000000 00000000 00000000 00000000 00000000 5f6fb709 0d893f64 4f121f81 4f730a48 +philox4x32 7 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff 5207ddc2 45165e59 4d8ee751 8c52f662 +philox4x32 7 243f6a88 85a308d3 13198a2e 03707344 a4093822 299f31d0 4dfccaba 190a87f0 c47362ba b6b5242a +philox4x32 10 00000000 00000000 00000000 00000000 00000000 00000000 6627e8d5 e169c58d bc57ac4c 9b00dbd8 +philox4x32 10 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff 408f276d 41c83b0e a20bc7c6 6d5451fd +philox4x32 10 243f6a88 85a308d3 13198a2e 03707344 a4093822 299f31d0 d16cfe09 94fdcceb 5001e420 24126ea1 +# +philox2x64 7 0000000000000000 0000000000000000 0000000000000000 b41da69fbfefc666 511e9ce1a5534056 +philox2x64 7 ffffffffffffffff ffffffffffffffff ffffffffffffffff a4696cc04462015d 724782dae17169e9 +philox2x64 7 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 98ed1534392bf372 67528b1568882fd5 +philox2x64 10 0000000000000000 0000000000000000 0000000000000000 ca00a0459843d731 66c24222c9a845b5 +philox2x64 10 ffffffffffffffff ffffffffffffffff ffffffffffffffff 65b021d60cd8310f 4d02f3222f86df20 +philox2x64 10 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 0a5e742c2997341c b0f883d38000de5d +# +philox4x64 7 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 5dc8ee6268ec62cd 139bc570b6c125a0 84d6deb4fb65f49e aff7583376d378c2 +philox4x64 7 ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff 071dd84367903154 48e2bbdc722b37d1 6afa9890bb89f76c 9194c8d8ada56ac7 +philox4x64 7 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 082efa98ec4e6c89 452821e638d01377 be5466cf34e90c6c 513a366704edf755 f05d9924c07044d3 bef2cb9cbea74c6c 8db948de4caa1f8a +philox4x64 10 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 16554d9eca36314c db20fe9d672d0fdc d7e772cee186176b 7e68b68aec7ba23b +philox4x64 10 ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff 87b092c3013fe90b 438c3c67be8d0224 9cc7d7c69cd777b6 a09caebf594f0ba0 +philox4x64 10 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 082efa98ec4e6c89 452821e638d01377 be5466cf34e90c6c a528f45403e61d95 38c72dbd566e9788 a5a1610e72fd18b5 57bd43b5e52b7fe6 +# +threefry2x32 13 00000000 00000000 00000000 00000000 9d1c5ec6 8bd50731 +threefry2x32 13 ffffffff ffffffff ffffffff ffffffff fd36d048 2d17272c +threefry2x32 13 243f6a88 85a308d3 13198a2e 03707344 ba3e4725 f27d669e +threefry2x32 20 00000000 00000000 00000000 00000000 6b200159 99ba4efe +threefry2x32 20 ffffffff ffffffff ffffffff ffffffff 1cb996fc bb002be7 +threefry2x32 20 243f6a88 85a308d3 13198a2e 03707344 c4923a9c 483df7a0 +threefry2x32 32 00000000 00000000 00000000 00000000 cee3d47e a23dfd5c +threefry2x32 32 ffffffff ffffffff ffffffff ffffffff 6e2fe0d0 b1b76f82 +threefry2x32 32 243f6a88 85a308d3 13198a2e 03707344 e2827716 c3c05cdf +# +threefry4x32 13 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 531c7e4f 39491ee5 2c855a92 3d6abf9a +threefry4x32 13 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff c4189358 1c9cc83a d5881c67 6a0a89e0 +threefry4x32 13 243f6a88 85a308d3 13198a2e 03707344 a4093822 299f31d0 082efa98 ec4e6c89 4aa71d8f 734738c2 431fc6a8 ae6debf1 +threefry4x32 20 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 9c6ca96a e17eae66 fc10ecd4 5256a7d8 +threefry4x32 20 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff 2a881696 57012287 f6c7446e a16a6732 +threefry4x32 20 243f6a88 85a308d3 13198a2e 03707344 a4093822 299f31d0 082efa98 ec4e6c89 59cd1dbb b8879579 86b5d00c ac8b6d84 +threefry4x32 72 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 93171da6 9220326d b392b7b1 ff58a002 +threefry4x32 72 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff 60743f3d 9961e684 aab21c34 8c65fb7d +threefry4x32 72 243f6a88 85a308d3 13198a2e 03707344 a4093822 299f31d0 082efa98 ec4e6c89 09930adf 7f27bd55 9ed68ce1 97f803f6 +# +threefry2x64 13 0000000000000000 0000000000000000 0000000000000000 0000000000000000 f167b032c3b480bd e91f9fee4b7a6fb5 +threefry2x64 13 ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ccdec5c917a874b1 4df53abca26ceb01 +threefry2x64 13 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 082efa98ec4e6c89 c3aac71561042993 3fe7ae8801aff316 +threefry2x64 20 0000000000000000 0000000000000000 0000000000000000 0000000000000000 c2b6e3a8c2c69865 6f81ed42f350084d +threefry2x64 20 ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff e02cb7c4d95d277a d06633d0893b8b68 +threefry2x64 20 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 082efa98ec4e6c89 263c7d30bb0f0af1 56be8361d3311526 +threefry2x64 32 0000000000000000 0000000000000000 0000000000000000 0000000000000000 38ba854d7f13cfb3 d02fca729d54fadc +threefry2x64 32 ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff 6b532f4f6e288646 0388f1ec135ee18e +threefry2x64 32 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 082efa98ec4e6c89 dad492f32efbd0c4 b6d7d0cd1f193e84 +# +threefry4x64 13 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 4071fabee1dc8e05 02ed3113695c9c62 397311b5b89f9d49 e21292c3258024bc +threefry4x64 13 ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff 7eaed935479722b5 90994358c429f31c 496381083e07a75b 627ed0d746821121 +threefry4x64 13 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 082efa98ec4e6c89 452821e638d01377 be5466cf34e90c6c c0ac29b7c97c50dd 3f84d5b5b5470917 4361288ef9c1900c 8717291521782833 0d19db18c20cf47e a0b41d63ac8581e5 +threefry4x64 20 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 09218ebde6c85537 55941f5266d86105 4bd25e16282434dc ee29ec846bd2e40b + threefry4x64 20 ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff 29c24097942bba1b 0371bbfb0f6f4e11 3c231ffa33f83a1c cd29113fde32d168 +threefry4x64 20 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 082efa98ec4e6c89 452821e638d01377 be5466cf34e90c6c be5466cf34e90c6c c0ac29b7c97c50dd a7e8fde591651bd9 baafd0c30138319b 84a5c1a729e685b9 901d406ccebc1ba4 +threefry4x64 72 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 94eeea8b1f2ada84 adf103313eae6670 952419a1f4b16d53 d83f13e63c9f6b11 +threefry4x64 72 ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff 11518c034bc1ff4c 193f10b8bcdcc9f7 d024229cb58f20d8 563ed6e48e05183f +threefry4x64 72 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 082efa98ec4e6c89 452821e638d01377 be5466cf34e90c6c be5466cf34e90c6c c0ac29b7c97c50dd acf412ccaa3b2270 c9e99bd53f2e9173 43dad469dc825948 fbb19d06c8a2b4dc \ No newline at end of file diff --git a/test/test_basic_rng/r123_rngNxW.mm b/test/test_basic_rng/r123_rngNxW.mm new file mode 100644 index 00000000..5062522d --- /dev/null +++ b/test/test_basic_rng/r123_rngNxW.mm @@ -0,0 +1,57 @@ +/* +Copyright 2010-2011, D. E. Shaw Research. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. + +* 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. + +* Neither the name of D. E. Shaw Research nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"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 THE COPYRIGHT +OWNER OR 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. +*/ + +/* + We need this file of some crazy logic in test_random123.cc. + It very deliberately DOES NOT use the "#pragma once" directive. + + Please just ignore any warning messages from code linters or + integrated development environments like VSCode. They won't be + able to correctly infer how we intend to use this file. +*/ + +RNGNxW_TPL(philox, 2, 32) +RNGNxW_TPL(philox, 4, 32) +RNGNxW_TPL(threefry, 2, 32) +RNGNxW_TPL(threefry, 4, 32) +#if R123_USE_64BIT +#if R123_USE_PHILOX_64BIT +RNGNxW_TPL(philox, 2, 64) +RNGNxW_TPL(philox, 4, 64) +#endif +RNGNxW_TPL(threefry, 2, 64) +RNGNxW_TPL(threefry, 4, 64) +#endif +#if R123_USE_AES_NI +RNGNxW_TPL(ars, 4, 32) +RNGNxW_TPL(aesni, 4, 32) +#endif diff --git a/test/test_basic_rng/test_r123_kat.cc b/test/test_basic_rng/test_r123_kat.cc new file mode 100644 index 00000000..0059aee7 --- /dev/null +++ b/test/test_basic_rng/test_r123_kat.cc @@ -0,0 +1,624 @@ +/* +Copyright 2010-2011, D. E. Shaw Research. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. + +* 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. + +* Neither the name of D. E. Shaw Research nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"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 THE COPYRIGHT +OWNER OR 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. +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +extern char *progname; +extern int debug; +extern int verbose; + +/* strdup may or may not be in string.h, depending on the value + of the pp-symbol _XOPEN_SOURCE and other arcana. Just + do it ourselves. + Mnemonic: "ntcs" = "nul-terminated character string" */ +char *ntcsdup(const char *s){ + char *p = (char *)malloc(strlen(s)+1); + strcpy(p, s); + return p; +} + +/* MSVC doesn't know about strtoull. Strictly speaking, strtoull + isn't standardized in C++98, either, but that seems not to be a + problem so we blissfully ignore it and use strtoull (or its MSVC + equivalent, _strtoui64) in both C and C++. If strtoull in C++ + becomes a problem, we can adopt the prtu strategy (see below) and + write C++ versions of strtouNN, that use an istringstream + instead. */ +#ifdef _MSC_FULL_VER +#define strtoull _strtoui64 +#endif +uint32_t strtou32(const char *p, char **endp, int base){ + uint32_t ret; + errno = 0; + ret = strtoul(p, endp, base); + assert(errno==0); + return ret; +} +uint64_t strtou64(const char *p, char **endp, int base){ + uint64_t ret; + errno = 0; + ret = strtoull(p, endp, base); + assert(errno==0); + return ret; +} + +/* Strict C++98 doesn't grok %llx or unsigned long long, and with + aggressive error-checking, e.g., g++ -pedantic -Wall, will refuse + to compile code like: + + fprintf(stderr, "%llx", (R123_ULONG_LONG)v); + + On the other hand, when compiling to a 32-bit target, the only + 64-bit type is long long, so we're out of luck if we can't use llx. + A portable, almost-standard way to do I/O on uint64_t values in C++ + is to use bona fide C++ I/O streams. We are still playing + fast-and-loose with standards because C++98 doesn't have + and hence doesn't even guarantee that there's a uint64_t, much less + that the insertion operator<<(ostream&) works correctly with + whatever we've typedef'ed to uint64_t in + . Hope for the best... */ +#include +#include +template +void prtu(T val){ + using namespace std; + cerr.width(std::numeric_limits::digits/4); + char prevfill = cerr.fill('0'); + ios_base::fmtflags prevflags = cerr.setf(ios_base::hex, ios_base::basefield); + cerr << val; + cerr.flags(prevflags); + cerr.fill(prevfill); + assert(!cerr.bad()); +} +void prtu32(uint32_t v){ prtu(v); } +void prtu64(uint64_t v){ prtu(v); } + + +// #define CHECKNOTEQUAL(x, y) do { if ((x) != (y)) ; else { \ +// FAIL() << "file " << __FILE__ << " line " << __LINE__ << " error " << #x << " == " << #y << ". Error code " << errno << "\n";\ +// exit(1); \ +// } } while (0) +// #define CHECKEQUAL(x, y) do { if ((x) == (y)) ; else { \ +// FAIL() << "file " << __FILE__ << " line " << __LINE__ << " error " << #x << " != " << #y << ". Error code " << errno << "\n";\ +// exit(1); \ +// } } while (0) +// #define CHECKZERO(x) CHECKEQUAL((x), 0) +// #define CHECKNOTZERO(x) CHECKNOTEQUAL((x), 0) + +#define dprintf(x) do { if (debug < 1) ; else { printf x; fflush(stdout); } } while (0) + +#define ALLZEROS(x, K, N) \ +do { \ + int allzeros = 1; \ + unsigned xi, xj; \ + for (xi = 0; xi < (unsigned)(K); xi++) \ + for (xj = 0; xj < (unsigned)(N); xj++) \ + allzeros = allzeros & ((x)[xi].v[xj] == 0); \ + if (allzeros) fprintf(stderr, "%s: Unexpected, all %lu elements of %ux%u had all zeros!\n", progname, (unsigned long)K, (unsigned)N, 8/*CHAR_BITS*/*(unsigned)sizeof(x[0].v[0])); \ +} while(0) + +// /* Read in N words of width W into ARR */ +// #define SCANFARRAY(ARR, NAME, N, W) \ +// do { \ +// int xi, xj; \ +// unsigned long long xv; \ +// for (xi = 0; xi < (N); xi++) { \ +// /* Avoid any cleverness with SCNx##W because Microsoft (as of Visual Studio 10.x) silently trashes the stack by pretending that %hhx is %x). */ \ +// const char *xfmt = " %llx%n"; \ +// ret = sscanf(cp, xfmt, &xv, &xj); \ +// ARR.v[xi] = (uint##W##_t)xv; \ +// if (debug > 1) printf("line %d: xfmt for W=%d is \"%s\", got ret=%d xj=%d, %s[%d]=%llx cp=%s", linenum, W, xfmt, ret, xj, #ARR, xi, (unsigned long long) ARR.v[xi], cp); \ +// if (ret < 1) { \ +// fprintf(stderr, "%s: ran out of words reading %s on line %d: " #NAME #N "x" #W " %2d %s", \ +// progname, #ARR, linenum, rounds, line); \ +// errs++; \ +// return; \ +// } \ +// cp += xj; \ +// } \ +// } while(0) + +#define PRINTARRAY(ARR, fp) \ +do { \ + char ofmt[64]; \ + size_t xj; \ + /* use %lu and the cast (instead of z) for portability to Microsoft, sizeof(v[0]) should fit easily in an unsigned long. Avoid inttypes for the same reason. */ \ + snprintf(ofmt, sizeof(ofmt), " %%0%lullx", (unsigned long)sizeof(ARR.v[0])*2UL); \ + for (xj = 0; xj < sizeof(ARR.v)/sizeof(ARR.v[0]); xj++) { \ + fprintf(fp, ofmt, (unsigned long long) ARR.v[xj]); \ + } \ +} while(0) + +#define PRINTLINE(NAME, N, W, R, ictr, ukey, octr, fp) \ +do { \ + fprintf(fp, "%s %d ", #NAME #N "x" #W, R); \ + PRINTARRAY(ictr, fp); \ + putc(' ', fp); \ + PRINTARRAY(ukey, fp); \ + putc(' ', fp); \ + PRINTARRAY(octr, fp); \ + putc('\n', fp); \ + fflush(fp); \ +} while(0) + +enum method_e{ +#define RNGNxW_TPL(base, N, W) base##N##x##W##_e, +#include "r123_rngNxW.mm" +#undef RNGNxW_TPL + last +}; + +#define RNGNxW_TPL(base, N, W) \ + typedef struct { \ + base##N##x##W##_ctr_t ctr; \ + base##N##x##W##_ukey_t ukey; \ + base##N##x##W##_ctr_t expected; \ + base##N##x##W##_ctr_t computed; \ + } base##N##x##W##_kat; +#include "r123_rngNxW.mm" +#undef RNGNxW_TPL + +typedef struct{ + enum method_e method; + unsigned nrounds; + union{ +#define RNGNxW_TPL(base, N, W) base##N##x##W##_kat base##N##x##W##_data; +#include "r123_rngNxW.mm" +#undef RNGNxW_TPL + /* Sigh... For those platforms that lack uint64_t, carve + out 128 bytes for the counter, key, expected, and computed. */ + char justbytes[128]; + }u; +} kat_instance; + + +#define LINESIZE 1024 + +int have_aesni = 0; +int verbose = 0; +int debug = 0; +unsigned nfailed = 0; +char *progname; + +extern void host_execute_tests(kat_instance *tests, unsigned ntests); + +/* A little hack to keep track of the test vectors that we don't know how to deal with: */ +int nunknowns = 0; +#define MAXUNKNOWNS 20 +const char *unknown_names[MAXUNKNOWNS]; +int unknown_counts[MAXUNKNOWNS]; + +void register_unknown(const char *name){ + int i; + for(i=0; i= MAXUNKNOWNS ){ + FAIL() << "Too many unknown rng types. Bye.\n"; + exit(1); + } + nunknowns++; + unknown_names[i] = ntcsdup(name); + unknown_counts[i] = 1; +} + +void report_unknowns(){ + int i; + for(i=0; iNxW */ +#define RNGNxW_TPL(base, N, W) \ +int read_##base##N##x##W(const char *line, kat_instance* tinst){ \ + size_t i; \ + int nchar; \ + const char *p = line; \ + char *newp; \ + size_t nkey = sizeof(tinst->u.base##N##x##W##_data.ukey.v)/sizeof(tinst->u.base##N##x##W##_data.ukey.v[0]); \ + tinst->method = base##N##x##W##_e; \ + sscanf(p, "%u%n", &tinst->nrounds, &nchar); \ + p += nchar; \ + for(i=0; iu.base##N##x##W##_data.ctr.v[i] = strtou##W(p, &newp, 16); \ + p = newp; \ + } \ + for(i=0; iu.base##N##x##W##_data.ukey.v[i] = strtou##W(p, &newp, 16); \ + p = newp; \ + } \ + for(i=0; iu.base##N##x##W##_data.expected.v[i] = strtou##W(p, &newp, 16); \ + p = newp; \ + } \ + /* set the computed to 0xca. If the test fails to set computed, we'll see cacacaca in the FAILURE notices */ \ + memset(tinst->u.base##N##x##W##_data.computed.v, 0xca, sizeof(tinst->u.base##N##x##W##_data.computed.v)); \ + return 1; \ +} +#include "r123_rngNxW.mm" +#undef RNGNxW_TPL + +/* readtest: dispatch to one of the read_NxW functions */ +static int readtest(const char *line, kat_instance* tinst){ + int nchar; + char name[LINESIZE]; + if( line[0] == '#') return 0; + sscanf(line, "%s%n", name, &nchar); + if(!have_aesni){ + /* skip any tests that require AESNI */ + if(strncmp(name, "aes", 3)==0 || + strncmp(name, "ars", 3)==0){ + register_unknown(name); + return 0; + } + } +#define RNGNxW_TPL(base, N, W) if(strcmp(name, #base #N "x" #W) == 0) return read_##base##N##x##W(line+nchar, tinst); +#include "r123_rngNxW.mm" +#undef RNGNxW_TPL + + register_unknown(name); + return 0; +} + +#define RNGNxW_TPL(base, N, W) \ +void report_##base##N##x##W##error(const kat_instance *ti){ \ + size_t i; \ + size_t nkey = sizeof(ti->u.base##N##x##W##_data.ukey.v)/sizeof(ti->u.base##N##x##W##_data.ukey.v[0]); \ + fprintf(stderr, "FAIL: expected: "); \ + fprintf(stderr, #base #N "x" #W " %d", ti->nrounds); \ + for(i=0; iu.base##N##x##W##_data.ctr.v[i]); \ + } \ + for(i=0; iu.base##N##x##W##_data.ukey.v[i]); \ + } \ + for(i=0; iu.base##N##x##W##_data.expected.v[i]); \ + } \ + fprintf(stderr, "\n"); \ + \ + fprintf(stderr, "FAIL: computed: "); \ + fprintf(stderr, #base #N "x" #W " %d", ti->nrounds); \ + for(i=0; iu.base##N##x##W##_data.ctr.v[i]); \ + } \ + for(i=0; iu.base##N##x##W##_data.ukey.v[i]); \ + } \ + for(i=0; iu.base##N##x##W##_data.computed.v[i]); \ + } \ + fprintf(stderr, "\n"); \ + nfailed++; \ +} +#include "r123_rngNxW.mm" +#undef RNGNxW_TPL + +// dispatch to one of the report_NxW() functions +void analyze_tests(const kat_instance *tests, unsigned ntests){ + unsigned i; + char zeros[512] = {0}; + for(i=0; iu.base##N##x##W##_data.expected.v, N*W/8)==0){ \ + fprintf(stderr, "kat expected all zeros? Something is wrong with the test harness!\n"); \ + nfailed++; \ + } \ + if (memcmp(ti->u.base##N##x##W##_data.computed.v, ti->u.base##N##x##W##_data.expected.v, N*W/8)) \ + report_##base##N##x##W##error(ti); \ + break; +#include "r123_rngNxW.mm" +#undef RNGNxW_TPL + case last: ; + } + } +} + +#define NTESTS 1000 + +void run() { + kat_instance *tests; + unsigned t, ntests = NTESTS; + char linebuf[LINESIZE]; + FILE *inpfile; + const char *p; + const char *inname; + // char filename[LINESIZE]; + + progname = "kat tests "; + inname = "./kat_vectors.txt"; + inpfile = fopen(inname, "r"); + if (inpfile == NULL) { + FAIL() << "Error opening input file " << inname << " for reading. Received error code " << errno << "\n"; + exit(1); + } + if ((p = getenv("KATC_VERBOSE")) != NULL) { + verbose = atoi(p); + } + if ((p = getenv("KATC_DEBUG")) != NULL) { + debug = atoi(p); + } + +#if R123_USE_AES_NI + have_aesni = haveAESNI(); +#else + have_aesni = 0; +#endif + + tests = (kat_instance *) malloc(sizeof(tests[0])*ntests); + if (tests == NULL) { + FAIL() << "Could not allocate " << (unsigned long) ntests << " bytes for tests\n"; + } + t = 0; + while (fgets(linebuf, sizeof linebuf, inpfile) != NULL) { + if( t == ntests ) { + ntests *= 2; + tests = (kat_instance *)realloc(tests, sizeof(tests[0])*ntests); + if (tests == NULL) { + FAIL() << "Could not grow tests to " << (unsigned long) ntests << " bytes.\n"; + } + } + if( readtest(linebuf, &tests[t]) ) + ++t; + } + if(t==ntests){ + FAIL() << "No more space for tests? Recompile with a larger NTESTS\n"; + exit(1); + } + tests[t].method = last; // N.B *not* t++ - the 'ntests' value passed to host_execute_tests does not count the 'last' one. + + report_unknowns(); + printf("Perform %lu tests.\n", (unsigned long)t); + host_execute_tests(tests, t); + + analyze_tests(tests, t); + free(tests); + if(nfailed != 0) + FAIL() << "Failed " << nfailed << " out of " << t << std::endl; + return; +} + + + +// With C++, it's a little trickier to create the mapping from +// method-name/round-count to functions +// because the round-counts are template arguments that have to be +// specified at compile-time. Thus, we can't just do #define RNGNxW_TPL +// and #include "r123_rngNxW.mm". We have to build a static map from: +// pair to functions that apply the right generator +// with the right number of rounds. + +#ifdef _MSC_FULL_VER +// Engines have multiple copy constructors, quite legal C++, disable MSVC complaint +#pragma warning (disable : 4521) +#endif + +#include +#include +#include +#include +#include +#include + +using namespace std; + +typedef map, void (*)(kat_instance *)> genmap_t; +genmap_t genmap; + +void dev_execute_tests(kat_instance *tests, unsigned ntests){ + unsigned i; + for(i=0; imethod, ti->nrounds)); + if(p == genmap.end()) + throw std::runtime_error("pair not in map. You probably need to add more genmap entries in kat_cpp.cpp"); + + p->second(ti); + // TODO: check that the corresponding Engine and MicroURNG + // return the same values. Note that we have ut_Engine and + // ut_MicroURNG, which check basic functionality, but they + // don't have the breadth of the kat_vectors. + } +} + +static int murng_reported; +static int engine_reported; + +template +void do_test(kat_instance* ti){ + GEN g; + struct gdata{ + typename GEN::ctr_type ctr; + typename GEN::ukey_type ukey; + typename GEN::ctr_type expected; + typename GEN::ctr_type computed; + }; + gdata data; + // use memcpy. A reinterpret_cast would violate strict aliasing. + memcpy(&data, &ti->u, sizeof(data)); + data.computed = g(data.ctr, data.ukey); + + // Before we return, let's make sure that MicroURNG and + // Engine work as expeccted. This doesn't really "fit" the + // execution model of kat.c, which just expects us to fill in + // ti->u.computed, so we report the error by failing to write back + // the computed data item in the (hopefully unlikely) event that + // things don't match up as expected. + int errs = 0; + + // MicroURNG: throws if the top 32 bits of the high word of ctr + // are non-zero. + typedef typename GEN::ctr_type::value_type value_type; + + value_type hibits = data.ctr[data.ctr.size()-1]>>( std::numeric_limits::digits - 32 ); + try{ + r123::MicroURNG urng(data.ctr, data.ukey); + if(hibits) + errs++; // Should have thrown. + for (size_t i = 0; i < data.expected.size(); i++) { + size_t j = data.expected.size() - i - 1; + if (data.expected[j] != urng()) { + errs++; + } + } + }catch(std::runtime_error& /*ignored*/){ + // A runtime_error is expected from the constructor + // when hibit is set. + if(!hibits) + errs++; + } + if(errs && (murng_reported++ == 0)) + cerr << "Error in MicroURNG, will appear as \"computed\" value of zero in error summary\n"; + + // Engine + // N.B. exercising discard() arguably belongs in ut_Engine.cpp + typedef r123::Engine Etype; + typedef typename GEN::ctr_type::value_type value_type; + Etype e(data.ukey); + typename GEN::ctr_type c = data.ctr; + value_type c0; + if( c[0] > 0 ){ + c0 = c[0]-1; + }else{ + // N.B. Assume that if c[0] is 0, then so are all the + // others. Arrange to "roll over" to {0,..,0} on the first + // counter-increment. Alternatively, we could just + // skip the test for this case... + c.fill(std::numeric_limits::max()); + c0 = c[0]; + } + c[0] /= 3; + e.setcounter(c, 0); + if( c0 > c[0] ){ + // skip one value by calling e() + (void)e(); + if (c0 > c[0]+1) { + // skip many values by calling discard() + R123_ULONG_LONG ndiscard = (c0 - c[0] - 1); + // Take care not to overflow the long long + if( ndiscard >= std::numeric_limits::max() / c.size() ){ + for(size_t j=0; j, will appear as \"computed\" value of zero in error summary\n"; + } + } + + // Signal an error to the caller by *not* copying back + // the computed data object into the ti + if(errs == 0) + memcpy(&ti->u, &data, sizeof(data)); +} + +void host_execute_tests(kat_instance *tests, unsigned ntests){ + // In C++1x, this could be staticly declared with an initializer list. + genmap[make_pair(threefry2x32_e, 13u)] = do_test >; + genmap[make_pair(threefry2x32_e, 20u)] = do_test >; + genmap[make_pair(threefry2x32_e, 32u)] = do_test >; +#if R123_USE_64BIT + genmap[make_pair(threefry2x64_e, 13u)] = do_test >; + genmap[make_pair(threefry2x64_e, 20u)] = do_test >; + genmap[make_pair(threefry2x64_e, 32u)] = do_test >; +#endif + + genmap[make_pair(threefry4x32_e, 13u)] = do_test >; + genmap[make_pair(threefry4x32_e, 20u)] = do_test >; + genmap[make_pair(threefry4x32_e, 72u)] = do_test >; +#if R123_USE_64BIT + genmap[make_pair(threefry4x64_e, 13u)] = do_test >; + genmap[make_pair(threefry4x64_e, 20u)] = do_test >; + genmap[make_pair(threefry4x64_e, 72u)] = do_test >; +#endif + + genmap[make_pair(philox2x32_e, 7u)] = do_test >; + genmap[make_pair(philox2x32_e, 10u)] = do_test >; + genmap[make_pair(philox4x32_e, 7u)] = do_test >; + genmap[make_pair(philox4x32_e, 10u)] = do_test >; + +#if R123_USE_PHILOX_64BIT + genmap[make_pair(philox2x64_e, 7u)] = do_test >; + genmap[make_pair(philox2x64_e, 10u)] = do_test >; + genmap[make_pair(philox4x64_e, 7u)] = do_test >; + genmap[make_pair(philox4x64_e, 10u)] = do_test >; +#endif + +#if R123_USE_AES_NI + genmap[make_pair(aesni4x32_e, 10u)] = do_test; + genmap[make_pair(ars4x32_e, 7u)] = do_test >; + genmap[make_pair(ars4x32_e, 10u)] = do_test >; +#endif + + dev_execute_tests(tests, ntests); +} + +class TestRandom123 : public ::testing::Test { }; + +TEST_F(TestRandom123, kat) { + run(); +} From be2cf88f22e11eee94e57539cae1191510c23d9b Mon Sep 17 00:00:00 2001 From: Riley Murray Date: Thu, 25 Jul 2024 13:50:07 -0400 Subject: [PATCH 11/15] Clean up Random123 tests and add the uniform histogram test. --- test/CMakeLists.txt | 2 + test/test_basic_rng/test_r123_kat.cc | 380 +++++++++++--------- test/test_basic_rng/ut_uniform_reference.mm | 19 + 3 files changed, 233 insertions(+), 168 deletions(-) create mode 100644 test/test_basic_rng/ut_uniform_reference.mm diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index dfe6f94b..7cc0cacd 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -60,9 +60,11 @@ if (GTest_FOUND) add_executable(RandBLAS_stats test_basic_rng/rng_common.hh test_basic_rng/test_sample_indices.cc + test_basic_rng/test_r123_kat.cc ) target_link_libraries(RandBLAS_stats RandBLAS GTest::GTest GTest::Main) gtest_discover_tests(RandBLAS_stats) + file(COPY test_basic_rng/kat_vectors.txt DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../) endif() message(STATUS "Checking for regression tests ... ${tmp}") diff --git a/test/test_basic_rng/test_r123_kat.cc b/test/test_basic_rng/test_r123_kat.cc index 0059aee7..3ae08099 100644 --- a/test/test_basic_rng/test_r123_kat.cc +++ b/test/test_basic_rng/test_r123_kat.cc @@ -43,10 +43,29 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include -extern char *progname; -extern int debug; -extern int verbose; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define LINESIZE 1024 + +#if R123_USE_AES_NI + int have_aesni = haveAESNI(); +#else + int have_aesni = 0; +#endif +int verbose = 0; +int debug = 0; /* strdup may or may not be in string.h, depending on the value of the pp-symbol _XOPEN_SOURCE and other arcana. Just @@ -58,13 +77,13 @@ char *ntcsdup(const char *s){ return p; } -/* MSVC doesn't know about strtoull. Strictly speaking, strtoull - isn't standardized in C++98, either, but that seems not to be a - problem so we blissfully ignore it and use strtoull (or its MSVC - equivalent, _strtoui64) in both C and C++. If strtoull in C++ - becomes a problem, we can adopt the prtu strategy (see below) and - write C++ versions of strtouNN, that use an istringstream - instead. */ +// Functions to read a (portion of) a string in a given base and convert it to +// an unsigned integer. +// +// These functions differ from std::from_chars in how they handle white space. +// Specifically, they strip leading whitespace, and then they stop reading as +// soon as they reach a non-numeric character. (Note that the "a" in 257a3673 +// counts as a numeric character if we're reading in hexadecimal format.) #ifdef _MSC_FULL_VER #define strtoull _strtoui64 #endif @@ -83,81 +102,16 @@ uint64_t strtou64(const char *p, char **endp, int base){ return ret; } -/* Strict C++98 doesn't grok %llx or unsigned long long, and with - aggressive error-checking, e.g., g++ -pedantic -Wall, will refuse - to compile code like: - - fprintf(stderr, "%llx", (R123_ULONG_LONG)v); - - On the other hand, when compiling to a 32-bit target, the only - 64-bit type is long long, so we're out of luck if we can't use llx. - A portable, almost-standard way to do I/O on uint64_t values in C++ - is to use bona fide C++ I/O streams. We are still playing - fast-and-loose with standards because C++98 doesn't have - and hence doesn't even guarantee that there's a uint64_t, much less - that the insertion operator<<(ostream&) works correctly with - whatever we've typedef'ed to uint64_t in - . Hope for the best... */ -#include -#include +// A helper function to print unsigned integers in hexadecimal format, with leading zeros if necessary. template -void prtu(T val){ - using namespace std; - cerr.width(std::numeric_limits::digits/4); - char prevfill = cerr.fill('0'); - ios_base::fmtflags prevflags = cerr.setf(ios_base::hex, ios_base::basefield); - cerr << val; - cerr.flags(prevflags); - cerr.fill(prevfill); - assert(!cerr.bad()); +void prtu(std::ostream& os, T val) { + os << std::hex << std::setw(std::numeric_limits::digits / 4) << std::setfill('0') << val; + assert(!os.bad()); } -void prtu32(uint32_t v){ prtu(v); } -void prtu64(uint64_t v){ prtu(v); } - -// #define CHECKNOTEQUAL(x, y) do { if ((x) != (y)) ; else { \ -// FAIL() << "file " << __FILE__ << " line " << __LINE__ << " error " << #x << " == " << #y << ". Error code " << errno << "\n";\ -// exit(1); \ -// } } while (0) -// #define CHECKEQUAL(x, y) do { if ((x) == (y)) ; else { \ -// FAIL() << "file " << __FILE__ << " line " << __LINE__ << " error " << #x << " != " << #y << ". Error code " << errno << "\n";\ -// exit(1); \ -// } } while (0) -// #define CHECKZERO(x) CHECKEQUAL((x), 0) -// #define CHECKNOTZERO(x) CHECKNOTEQUAL((x), 0) - -#define dprintf(x) do { if (debug < 1) ; else { printf x; fflush(stdout); } } while (0) - -#define ALLZEROS(x, K, N) \ -do { \ - int allzeros = 1; \ - unsigned xi, xj; \ - for (xi = 0; xi < (unsigned)(K); xi++) \ - for (xj = 0; xj < (unsigned)(N); xj++) \ - allzeros = allzeros & ((x)[xi].v[xj] == 0); \ - if (allzeros) fprintf(stderr, "%s: Unexpected, all %lu elements of %ux%u had all zeros!\n", progname, (unsigned long)K, (unsigned)N, 8/*CHAR_BITS*/*(unsigned)sizeof(x[0].v[0])); \ -} while(0) - -// /* Read in N words of width W into ARR */ -// #define SCANFARRAY(ARR, NAME, N, W) \ -// do { \ -// int xi, xj; \ -// unsigned long long xv; \ -// for (xi = 0; xi < (N); xi++) { \ -// /* Avoid any cleverness with SCNx##W because Microsoft (as of Visual Studio 10.x) silently trashes the stack by pretending that %hhx is %x). */ \ -// const char *xfmt = " %llx%n"; \ -// ret = sscanf(cp, xfmt, &xv, &xj); \ -// ARR.v[xi] = (uint##W##_t)xv; \ -// if (debug > 1) printf("line %d: xfmt for W=%d is \"%s\", got ret=%d xj=%d, %s[%d]=%llx cp=%s", linenum, W, xfmt, ret, xj, #ARR, xi, (unsigned long long) ARR.v[xi], cp); \ -// if (ret < 1) { \ -// fprintf(stderr, "%s: ran out of words reading %s on line %d: " #NAME #N "x" #W " %2d %s", \ -// progname, #ARR, linenum, rounds, line); \ -// errs++; \ -// return; \ -// } \ -// cp += xj; \ -// } \ -// } while(0) +// Specializations for uint32_t and uint64_t +void prtu32(std::ostream& os, uint32_t v) { prtu(os, v); } +void prtu64(std::ostream& os, uint64_t v) { prtu(os, v); } #define PRINTARRAY(ARR, fp) \ do { \ @@ -212,45 +166,31 @@ typedef struct{ }u; } kat_instance; - -#define LINESIZE 1024 - -int have_aesni = 0; -int verbose = 0; -int debug = 0; -unsigned nfailed = 0; -char *progname; - -extern void host_execute_tests(kat_instance *tests, unsigned ntests); +void host_execute_tests(kat_instance *tests, unsigned ntests); -/* A little hack to keep track of the test vectors that we don't know how to deal with: */ -int nunknowns = 0; +/* Keep track of the test vectors that we don't know how to deal with: */ #define MAXUNKNOWNS 20 -const char *unknown_names[MAXUNKNOWNS]; -int unknown_counts[MAXUNKNOWNS]; -void register_unknown(const char *name){ +struct UnknownKatTracker { + int num_unknowns; + const char *unknown_names[MAXUNKNOWNS]; + int unknown_counts[MAXUNKNOWNS]; +}; + +void register_unknown(UnknownKatTracker &ukt, const char *name){ int i; - for(i=0; i= MAXUNKNOWNS ){ FAIL() << "Too many unknown rng types. Bye.\n"; - exit(1); - } - nunknowns++; - unknown_names[i] = ntcsdup(name); - unknown_counts[i] = 1; -} - -void report_unknowns(){ - int i; - for(i=0; iNxW */ @@ -283,8 +223,9 @@ int read_##base##N##x##W(const char *line, kat_instance* tinst){ \ #include "r123_rngNxW.mm" #undef RNGNxW_TPL + /* readtest: dispatch to one of the read_NxW functions */ -static int readtest(const char *line, kat_instance* tinst){ +static int readtest(UnknownKatTracker &ukt, const char *line, kat_instance* tinst){ int nchar; char name[LINESIZE]; if( line[0] == '#') return 0; @@ -293,7 +234,7 @@ static int readtest(const char *line, kat_instance* tinst){ /* skip any tests that require AESNI */ if(strncmp(name, "aes", 3)==0 || strncmp(name, "ars", 3)==0){ - register_unknown(name); + register_unknown(ukt, name); return 0; } } @@ -301,46 +242,48 @@ static int readtest(const char *line, kat_instance* tinst){ #include "r123_rngNxW.mm" #undef RNGNxW_TPL - register_unknown(name); + register_unknown(ukt, name); return 0; } #define RNGNxW_TPL(base, N, W) \ -void report_##base##N##x##W##error(const kat_instance *ti){ \ +void report_##base##N##x##W##error(int &nfailed, const kat_instance *ti){ \ size_t i; \ size_t nkey = sizeof(ti->u.base##N##x##W##_data.ukey.v)/sizeof(ti->u.base##N##x##W##_data.ukey.v[0]); \ - fprintf(stderr, "FAIL: expected: "); \ - fprintf(stderr, #base #N "x" #W " %d", ti->nrounds); \ - for(i=0; iu.base##N##x##W##_data.ctr.v[i]); \ - } \ - for(i=0; iu.base##N##x##W##_data.ukey.v[i]); \ - } \ - for(i=0; iu.base##N##x##W##_data.expected.v[i]); \ - } \ - fprintf(stderr, "\n"); \ - \ - fprintf(stderr, "FAIL: computed: "); \ - fprintf(stderr, #base #N "x" #W " %d", ti->nrounds); \ - for(i=0; iu.base##N##x##W##_data.ctr.v[i]); \ - } \ - for(i=0; iu.base##N##x##W##_data.ukey.v[i]); \ - } \ - for(i=0; iu.base##N##x##W##_data.computed.v[i]); \ - } \ - fprintf(stderr, "\n"); \ - nfailed++; \ + std::stringstream ss; \ + ss << "FAIL: expected: "; \ + ss << #base #N "x" #W " " << ti->nrounds; \ + for(i=0; iu.base##N##x##W##_data.ctr.v[i]); \ + } \ + for(i=0; iu.base##N##x##W##_data.ukey.v[i]); \ + } \ + for(i=0; iu.base##N##x##W##_data.expected.v[i]); \ + } \ + ss << "\n"; \ + \ + ss << "FAIL: computed: "; \ + ss << #base #N "x" #W " " << ti->nrounds; \ + for(i=0; iu.base##N##x##W##_data.ctr.v[i]); \ + } \ + for(i=0; iu.base##N##x##W##_data.ukey.v[i]); \ + } \ + for(i=0; iu.base##N##x##W##_data.computed.v[i]); \ + } \ + ss << "\n"; \ + FAIL() << ss.str(); \ + nfailed++; \ } #include "r123_rngNxW.mm" #undef RNGNxW_TPL // dispatch to one of the report_NxW() functions -void analyze_tests(const kat_instance *tests, unsigned ntests){ +void analyze_tests(int &nfailed, const kat_instance *tests, unsigned ntests){ unsigned i; char zeros[512] = {0}; for(i=0; iu.base##N##x##W##_data.expected.v, N*W/8)==0){ \ - fprintf(stderr, "kat expected all zeros? Something is wrong with the test harness!\n"); \ + FAIL() << "kat expected all zeros? Something is wrong with the test harness!\n"; \ nfailed++; \ } \ if (memcmp(ti->u.base##N##x##W##_data.computed.v, ti->u.base##N##x##W##_data.expected.v, N*W/8)) \ - report_##base##N##x##W##error(ti); \ + report_##base##N##x##W##error(nfailed, ti); \ break; #include "r123_rngNxW.mm" #undef RNGNxW_TPL @@ -363,34 +306,27 @@ void analyze_tests(const kat_instance *tests, unsigned ntests){ #define NTESTS 1000 -void run() { +void run_base_rng_kat() { kat_instance *tests; unsigned t, ntests = NTESTS; char linebuf[LINESIZE]; FILE *inpfile; const char *p; const char *inname; - // char filename[LINESIZE]; - - progname = "kat tests "; + int nfailed; + + UnknownKatTracker ukt{}; + inname = "./kat_vectors.txt"; inpfile = fopen(inname, "r"); - if (inpfile == NULL) { + if (inpfile == NULL) FAIL() << "Error opening input file " << inname << " for reading. Received error code " << errno << "\n"; - exit(1); - } - if ((p = getenv("KATC_VERBOSE")) != NULL) { + + if ((p = getenv("KATC_VERBOSE")) != NULL) verbose = atoi(p); - } - if ((p = getenv("KATC_DEBUG")) != NULL) { + + if ((p = getenv("KATC_DEBUG")) != NULL) debug = atoi(p); - } - -#if R123_USE_AES_NI - have_aesni = haveAESNI(); -#else - have_aesni = 0; -#endif tests = (kat_instance *) malloc(sizeof(tests[0])*ntests); if (tests == NULL) { @@ -405,20 +341,21 @@ void run() { FAIL() << "Could not grow tests to " << (unsigned long) ntests << " bytes.\n"; } } - if( readtest(linebuf, &tests[t]) ) + if( readtest(ukt, linebuf, &tests[t]) ) ++t; } if(t==ntests){ FAIL() << "No more space for tests? Recompile with a larger NTESTS\n"; - exit(1); } tests[t].method = last; // N.B *not* t++ - the 'ntests' value passed to host_execute_tests does not count the 'last' one. - report_unknowns(); + for(int i=0; i< ukt.num_unknowns; ++i){ + printf("%d test vectors of type %s skipped\n", ukt.unknown_counts[i], ukt.unknown_names[i]); + } printf("Perform %lu tests.\n", (unsigned long)t); host_execute_tests(tests, t); - analyze_tests(tests, t); + analyze_tests(nfailed, tests, t); free(tests); if(nfailed != 0) FAIL() << "Failed " << nfailed << " out of " << t << std::endl; @@ -617,8 +554,115 @@ void host_execute_tests(kat_instance *tests, unsigned ntests){ dev_execute_tests(tests, ntests); } -class TestRandom123 : public ::testing::Test { }; -TEST_F(TestRandom123, kat) { - run(); +using namespace r123; + +template +typename r123::make_unsigned::type U(T x){ return x; } + +template +typename r123::make_signed::type S(T x){ return x; } + +#define Chk(u, Rng, Ftype, _nfail_, _refhist_) do{ \ + chk(#u, #Rng, #Ftype, &u, _nfail_, _refhist_); \ + }while(0) + +void RefHist(std::map &refmap, const char* k, const char *v){ + refmap[std::string(k)] = std::string(v); +} + +void fillrefhist(std::map &refmap){ + #include "ut_uniform_reference.mm" +} + +template +void chk(const std::string& fname, const std::string& rngname, const std::string& ftypename, Utype f, int &nfail, std::map &refmap){ + std::string key = fname + " " + rngname + " " + ftypename; + RNG rng; + typedef typename RNG::ukey_type ukey_type; + typedef typename RNG::ctr_type ctr_type; + typedef typename RNG::key_type key_type; + + ctr_type c = {{}}; + ukey_type uk = {{}}; + key_type k = uk; + // 26 bins - 13 greater than 0 and 13 less. Why 13? Because a + // prime number seems less likely to tickle the rounding-related + // corner cases, which is aruably both good and bad. + const int NBINS=26; + + int hist[NBINS] = {}; + for(int i=0; i<1000; ++i){ + c = c.incr(); + ctr_type r = rng(c, k); + for(int j=0; j= -1.); + R123_ASSERT( u <= 1.); + int idx = (int) ((u + Ftype(1.))*Ftype(NBINS/2)); + hist[idx]++; + } + } + std::ostringstream oss; + for(int i=0; i Date: Thu, 25 Jul 2024 14:03:07 -0400 Subject: [PATCH 12/15] clean up --- test/test_basic_rng/test_r123_kat.cc | 31 +++++++++++++++------ test/test_basic_rng/ut_uniform_reference.mm | 19 ------------- 2 files changed, 23 insertions(+), 27 deletions(-) delete mode 100644 test/test_basic_rng/ut_uniform_reference.mm diff --git a/test/test_basic_rng/test_r123_kat.cc b/test/test_basic_rng/test_r123_kat.cc index 3ae08099..ed8c979f 100644 --- a/test/test_basic_rng/test_r123_kat.cc +++ b/test/test_basic_rng/test_r123_kat.cc @@ -567,12 +567,28 @@ typename r123::make_signed::type S(T x){ return x; } chk(#u, #Rng, #Ftype, &u, _nfail_, _refhist_); \ }while(0) -void RefHist(std::map &refmap, const char* k, const char *v){ - refmap[std::string(k)] = std::string(v); -} - -void fillrefhist(std::map &refmap){ - #include "ut_uniform_reference.mm" +std::map get_refmap(){ + using std::string; + std::map refmap{}; + refmap[string("u01 Threefry4x32 float")] = string(" 0 0 0 0 0 0 0 0 0 0 0 0 0 301 330 326 320 295 291 298 287 305 307 310 316 314"); + refmap[string("u01 Threefry4x32 double")]= string(" 0 0 0 0 0 0 0 0 0 0 0 0 0 301 330 326 320 295 291 298 287 305 307 310 316 314"); + refmap[string("u01 Threefry4x32 long double")] = string(" 0 0 0 0 0 0 0 0 0 0 0 0 0 301 330 326 320 295 291 298 287 305 307 310 316 314"); + refmap[string("u01 Threefry4x64 float")] = string(" 0 0 0 0 0 0 0 0 0 0 0 0 0 308 295 322 300 316 291 311 289 346 297 310 340 275"); + refmap[string("u01 Threefry4x64 double")] = string(" 0 0 0 0 0 0 0 0 0 0 0 0 0 308 295 322 300 316 291 311 289 346 297 310 340 275"); + refmap[string("u01 Threefry4x64 long double")] = string(" 0 0 0 0 0 0 0 0 0 0 0 0 0 308 295 322 300 316 291 311 289 346 297 310 340 275"); + refmap[string("uneg11 Threefry4x32 float")] = string(" 156 139 148 146 159 148 159 168 142 160 156 161 153 143 158 150 180 174 152 163 157 129 166 151 140 142"); + refmap[string("uneg11 Threefry4x32 double")] = string(" 156 139 148 146 159 148 159 168 142 160 156 161 153 143 158 150 180 174 152 163 157 129 166 151 140 142"); + refmap[string("uneg11 Threefry4x32 long double")] = string( " 156 139 148 146 159 148 159 168 142 160 156 161 153 143 158 150 180 174 152 163 157 129 166 151 140 142"); + refmap[string("uneg11 Threefry4x64 float")] = string( " 159 141 148 184 162 142 155 137 173 187 153 140 135 164 144 146 149 151 171 152 148 137 179 146 145 152"); + refmap[string("uneg11 Threefry4x64 double")] = string( " 159 141 148 184 162 142 155 137 173 187 153 140 135 164 144 146 149 151 171 152 148 137 179 146 145 152"); + refmap[string("uneg11 Threefry4x64 long double")] = string( " 159 141 148 184 162 142 155 137 173 187 153 140 135 164 144 146 149 151 171 152 148 137 179 146 145 152"); + refmap[string("u01fixedpt Threefry4x32 float")] = string( " 0 0 0 0 0 0 0 0 0 0 0 0 0 301 330 326 320 295 291 298 287 305 307 310 316 314"); + refmap[string("u01fixedpt Threefry4x32 double")] = string( " 0 0 0 0 0 0 0 0 0 0 0 0 0 301 330 326 320 295 291 298 287 305 307 310 316 314"); + refmap[string("u01fixedpt Threefry4x32 long double")] = string( " 0 0 0 0 0 0 0 0 0 0 0 0 0 301 330 326 320 295 291 298 287 305 307 310 316 314"); + refmap[string("u01fixedpt Threefry4x64 float")] = string( " 0 0 0 0 0 0 0 0 0 0 0 0 0 308 295 322 300 316 291 311 289 346 297 310 340 275"); + refmap[string("u01fixedpt Threefry4x64 double")] = string( " 0 0 0 0 0 0 0 0 0 0 0 0 0 308 295 322 300 316 291 311 289 346 297 310 340 275"); + refmap[string("u01fixedpt Threefry4x64 long double")] = string( " 0 0 0 0 0 0 0 0 0 0 0 0 0 308 295 322 300 316 291 311 289 346 297 310 340 275"); + return refmap; } template @@ -617,8 +633,7 @@ void chk(const std::string& fname, const std::string& rngname, const std::string } void run_ut_uniform(){ - std::map refmap{}; - fillrefhist(refmap); + auto refmap = get_refmap(); int nfail = 0; // 18 tests: 3 functions (u01, uneg11, u01fixedpt) // x 2 input sizes (32 bit or 64 bit) diff --git a/test/test_basic_rng/ut_uniform_reference.mm b/test/test_basic_rng/ut_uniform_reference.mm deleted file mode 100644 index e6a31f21..00000000 --- a/test/test_basic_rng/ut_uniform_reference.mm +++ /dev/null @@ -1,19 +0,0 @@ -RefHist(refmap, "u01 Threefry4x32 float", " 0 0 0 0 0 0 0 0 0 0 0 0 0 301 330 326 320 295 291 298 287 305 307 310 316 314"); -RefHist(refmap, "u01 Threefry4x32 double", " 0 0 0 0 0 0 0 0 0 0 0 0 0 301 330 326 320 295 291 298 287 305 307 310 316 314"); -RefHist(refmap, "u01 Threefry4x32 long double", " 0 0 0 0 0 0 0 0 0 0 0 0 0 301 330 326 320 295 291 298 287 305 307 310 316 314"); -RefHist(refmap, "u01 Threefry4x64 float", " 0 0 0 0 0 0 0 0 0 0 0 0 0 308 295 322 300 316 291 311 289 346 297 310 340 275"); -RefHist(refmap, "u01 Threefry4x64 double", " 0 0 0 0 0 0 0 0 0 0 0 0 0 308 295 322 300 316 291 311 289 346 297 310 340 275"); -RefHist(refmap, "u01 Threefry4x64 long double", " 0 0 0 0 0 0 0 0 0 0 0 0 0 308 295 322 300 316 291 311 289 346 297 310 340 275"); -RefHist(refmap, "uneg11 Threefry4x32 float", " 156 139 148 146 159 148 159 168 142 160 156 161 153 143 158 150 180 174 152 163 157 129 166 151 140 142"); -RefHist(refmap, "uneg11 Threefry4x32 double", " 156 139 148 146 159 148 159 168 142 160 156 161 153 143 158 150 180 174 152 163 157 129 166 151 140 142"); -RefHist(refmap, "uneg11 Threefry4x32 long double", " 156 139 148 146 159 148 159 168 142 160 156 161 153 143 158 150 180 174 152 163 157 129 166 151 140 142"); -RefHist(refmap, "uneg11 Threefry4x64 float", " 159 141 148 184 162 142 155 137 173 187 153 140 135 164 144 146 149 151 171 152 148 137 179 146 145 152"); -RefHist(refmap, "uneg11 Threefry4x64 double", " 159 141 148 184 162 142 155 137 173 187 153 140 135 164 144 146 149 151 171 152 148 137 179 146 145 152"); -RefHist(refmap, "uneg11 Threefry4x64 long double", " 159 141 148 184 162 142 155 137 173 187 153 140 135 164 144 146 149 151 171 152 148 137 179 146 145 152"); -RefHist(refmap, "u01fixedpt Threefry4x32 float", " 0 0 0 0 0 0 0 0 0 0 0 0 0 301 330 326 320 295 291 298 287 305 307 310 316 314"); -RefHist(refmap, "u01fixedpt Threefry4x32 double", " 0 0 0 0 0 0 0 0 0 0 0 0 0 301 330 326 320 295 291 298 287 305 307 310 316 314"); -RefHist(refmap, "u01fixedpt Threefry4x32 long double", " 0 0 0 0 0 0 0 0 0 0 0 0 0 301 330 326 320 295 291 298 287 305 307 310 316 314"); -RefHist(refmap, "u01fixedpt Threefry4x64 float", " 0 0 0 0 0 0 0 0 0 0 0 0 0 308 295 322 300 316 291 311 289 346 297 310 340 275"); -RefHist(refmap, "u01fixedpt Threefry4x64 double", " 0 0 0 0 0 0 0 0 0 0 0 0 0 308 295 322 300 316 291 311 289 346 297 310 340 275"); -RefHist(refmap, "u01fixedpt Threefry4x64 long double", " 0 0 0 0 0 0 0 0 0 0 0 0 0 308 295 322 300 316 291 311 289 346 297 310 340 275"); - From 48774eb95716af3c0d8542a2625f75178bee24dd Mon Sep 17 00:00:00 2001 From: Riley Murray Date: Thu, 25 Jul 2024 14:36:48 -0400 Subject: [PATCH 13/15] tweak file location --- test/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7cc0cacd..c1c39ed7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -64,7 +64,8 @@ if (GTest_FOUND) ) target_link_libraries(RandBLAS_stats RandBLAS GTest::GTest GTest::Main) gtest_discover_tests(RandBLAS_stats) - file(COPY test_basic_rng/kat_vectors.txt DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../) + file(COPY test_basic_rng/kat_vectors.txt DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../test/) + file(COPY test_basic_rng/kat_vectors.txt DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) endif() message(STATUS "Checking for regression tests ... ${tmp}") From 04610302839a7d7354a19b153a56359108beeaaa Mon Sep 17 00:00:00 2001 From: Riley Murray Date: Thu, 25 Jul 2024 14:44:13 -0400 Subject: [PATCH 14/15] initializing variables is important --- test/test_basic_rng/test_r123_kat.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_basic_rng/test_r123_kat.cc b/test/test_basic_rng/test_r123_kat.cc index ed8c979f..af375785 100644 --- a/test/test_basic_rng/test_r123_kat.cc +++ b/test/test_basic_rng/test_r123_kat.cc @@ -172,7 +172,7 @@ void host_execute_tests(kat_instance *tests, unsigned ntests); #define MAXUNKNOWNS 20 struct UnknownKatTracker { - int num_unknowns; + int num_unknowns = 0; const char *unknown_names[MAXUNKNOWNS]; int unknown_counts[MAXUNKNOWNS]; }; From 5fb5fadae7f5b4be8d44a3631d9c4c34333c5a67 Mon Sep 17 00:00:00 2001 From: Riley Murray Date: Thu, 25 Jul 2024 14:47:48 -0400 Subject: [PATCH 15/15] more variable initialization --- test/test_basic_rng/test_r123_kat.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_basic_rng/test_r123_kat.cc b/test/test_basic_rng/test_r123_kat.cc index af375785..14e6517e 100644 --- a/test/test_basic_rng/test_r123_kat.cc +++ b/test/test_basic_rng/test_r123_kat.cc @@ -313,7 +313,7 @@ void run_base_rng_kat() { FILE *inpfile; const char *p; const char *inname; - int nfailed; + int nfailed = 0; UnknownKatTracker ukt{};