star-sp

Random number generators and distributions
git clone git://git.meso-star.fr/star-sp.git
Log | Files | Refs | README | LICENSE

commit ae7e4d277f18694008077cb9a5230f5ab361c84b
parent 9d5495061132471fcece2919d05d993b5b51cb3a
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Mon,  4 Jul 2016 16:19:13 +0200

Starting version 0.4, that is mainly about distributions.

This first commit adds a gaussian/normal distribution with its test.

Diffstat:
Mcmake/CMakeLists.txt | 23+++++++++++++++++------
Msrc/ssp.h | 28++++++++++++++++++++++++++++
Asrc/ssp_distributions.cpp | 118+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ssp_distributions.h | 22++++++++++++++++++++++
Msrc/ssp_rng.c | 35-----------------------------------
Msrc/ssp_rng_c.h | 35+++++++++++++++++++++++++++++++++++
Asrc/test_ssp_distrib_gaussian.c | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 326 insertions(+), 41 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -66,14 +66,24 @@ rcmake_append_runtime_dirs(_runtime_dirs RSys ${Boost_LIBRARY_DIRS}) # Configure and define targets ################################################################################ set(VERSION_MAJOR 0) -set(VERSION_MINOR 3) +set(VERSION_MINOR 4) set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) -set(SSP_FILES_SRC ssp_ran_discrete.c ssp_rng.c ssp_rng_proxy.c) -set(SSP_FILES_INC ssp_rng_c.h) -set(SSP_FILES_INC_API ssp.h) -set(SSP_FILES_DOC COPYING.en COPYING.fr README.md) +set(SSP_FILES_SRC + ssp_ran_discrete.c + ssp_rng.c + ssp_rng_proxy.c + ssp_distributions.cpp) +set(SSP_FILES_INC + ssp_rng_c.h + ssp_distributions.h) +set(SSP_FILES_INC_API + ssp.h) +set(SSP_FILES_DOC + COPYING.en + COPYING.fr + README.md) # Prepend each file in the `SSP_FILES_<SRC|INC>' list by `SSP_SOURCE_DIR' rcmake_prepend_path(SSP_FILES_SRC ${SSP_SOURCE_DIR}) @@ -108,7 +118,7 @@ if(NOT NO_TEST) add_executable(${_name} ${SSP_SOURCE_DIR}/${_name}.c ${SSP_SOURCE_DIR}/test_ssp_utils.h) - target_link_libraries(${_name} ssp RSys) + target_link_libraries(${_name} ssp RSys ${MATH_LIB}) set(_libraries ${ARGN}) foreach(_lib ${_libraries}) target_link_libraries(${_name} ${_lib}) @@ -145,6 +155,7 @@ if(NOT NO_TEST) new_test(test_ssp_ran_hg ${MATH_LIB}) new_test(test_ssp_ran_triangle ${MATH_LIB}) new_test(test_ssp_rng_proxy) + new_test(test_ssp_distrib_gaussian) endif() ################################################################################ diff --git a/src/ssp.h b/src/ssp.h @@ -58,6 +58,7 @@ struct ssp_rng; struct ssp_rng_proxy; struct ssp_ran_discrete; +struct ssp_distrib_gaussian; /* Forward declaration of external types */ struct mem_allocator; @@ -577,6 +578,33 @@ ssp_ran_sphere_hg return f33_mulf3(sample, f33_basis(basis, up), sample_local); } +/******************************************************************************* + * Gaussian distribution + ******************************************************************************/ + +SSP_API res_T +ssp_distribution_gaussian_init +(struct mem_allocator* allocator, + double mu, + double sigma, + struct ssp_distrib_gaussian **out_dist); + +SSP_API res_T +ssp_distribution_gaussian_ref_get +(struct ssp_distrib_gaussian* distrib); + +SSP_API res_T +ssp_distribution_gaussian_ref_put +(struct ssp_distrib_gaussian* distrib); + +SSP_API double +ssp_distribution_gaussian_get +(const struct ssp_distrib_gaussian *dist, struct ssp_rng* rng); + +SSP_API double +ssp_distribution_gaussian_pdf +(const struct ssp_distrib_gaussian *dist, double x); + END_DECLS #endif /* SSP_H */ diff --git a/src/ssp_distributions.cpp b/src/ssp_distributions.cpp @@ -0,0 +1,118 @@ +#include "ssp_distributions.h" + +#include <rsys/mem_allocator.h> +#include <rsys/math.h> +#include "ssp_rng_c.h" + +using normal_dist_double=RAN_NAMESPACE::normal_distribution<double>; + +struct ssp_distrib_gaussian_state +{ + normal_dist_double *distribution; + double mu; + double K1; // 1.0 / sigma; + double K2; // 1.0 / (sigma * SQRT_2_PI) +}; + +static void +distrib_gaussian_release(ref_T* ref) +{ + ssp_distrib_gaussian* distrib; + ASSERT(ref); + distrib = CONTAINER_OF(ref, struct ssp_distrib_gaussian, ref); + if(distrib->state) { + distrib->state->distribution->~normal_distribution<double>(); + MEM_RM(distrib->allocator, distrib->state->distribution); + MEM_RM(distrib->allocator, distrib->state); + } + MEM_RM(distrib->allocator, distrib); +} + +/******************************************************************************* + * Gaussian distribution exported functions + ******************************************************************************/ + +res_T +ssp_distribution_gaussian_init +(struct mem_allocator* allocator, + double mu, + double sigma, + struct ssp_distrib_gaussian **out_dist) +{ + ssp_distrib_gaussian *dist = nullptr; + void *tmp = nullptr; + res_T res = RES_OK; + + if (sigma < 0) + return RES_BAD_ARG; + + allocator = allocator ? allocator : &mem_default_allocator; + + dist = static_cast<ssp_distrib_gaussian*>( + MEM_CALLOC(allocator, 1, sizeof(ssp_distrib_gaussian))); + if (!dist) { + res = RES_MEM_ERR; + goto err; + } + ref_init(&dist->ref); + dist->state = static_cast<ssp_distrib_gaussian_state*>( + MEM_CALLOC(allocator, 1, sizeof(ssp_distrib_gaussian_state))); + if (!dist->state) { + res = RES_MEM_ERR; + goto err; + } + tmp = MEM_ALLOC(allocator, sizeof(normal_dist_double)); + if (!tmp) { + res = RES_MEM_ERR; + goto err; + } + dist->allocator = allocator; + dist->state->distribution = static_cast<normal_dist_double*>( + new(tmp) normal_dist_double(mu, sigma)); + dist->state->mu = mu; + dist->state->K1 = 1.0 / sigma; + dist->state->K2 = 1.0 / (sigma * SQRT_2_PI); + if (out_dist) *out_dist = dist; + end: + return res; + err: + if (dist) { + ref_put(&dist->ref, distrib_gaussian_release); + dist = nullptr; + } + goto end; +} + +res_T +ssp_distribution_gaussian_ref_get +(struct ssp_distrib_gaussian* distrib) +{ + if(!distrib) return RES_BAD_ARG; + ref_get(&distrib->ref); + return RES_OK; +} + +res_T +ssp_distribution_gaussian_ref_put +(struct ssp_distrib_gaussian* distrib) +{ + if(!distrib) return RES_BAD_ARG; + ref_put(&distrib->ref, distrib_gaussian_release); + return RES_OK; +} + +double +ssp_distribution_gaussian_get +(const struct ssp_distrib_gaussian *dist, struct ssp_rng* rng) +{ + class rng_cxx r(*rng); + return dist->state->distribution->operator()(r); +} + +double +ssp_distribution_gaussian_pdf +(const struct ssp_distrib_gaussian *dist, double x) +{ + const double tmp = (x - dist->state->mu) * dist->state->K1; + return dist->state->K2 * exp(-0.5 * tmp * tmp); +} diff --git a/src/ssp_distributions.h b/src/ssp_distributions.h @@ -0,0 +1,22 @@ +#ifndef __SSP_DISTRIB_GAUSSIAN_H__ +#define __SSP_DISTRIB_GAUSSIAN_H__ + +#include <rsys/rsys.h> +#include <rsys/ref_count.h> + +/* Forward declaration of opaque types */ +struct ssp_distrib_gaussian_state; + +/* Forward declaration of external types */ +struct ssp_rng; +struct mem_allocator; + +struct ssp_distrib_gaussian { + size_t sizeof_params; + size_t sizeof_state; + ref_T ref; + struct mem_allocator* allocator; + struct ssp_distrib_gaussian_state *state; +}; + +#endif diff --git a/src/ssp_rng.c b/src/ssp_rng.c @@ -34,25 +34,6 @@ #include <rsys/dynamic_array_char.h> #include <rsys/mem_allocator.h> -#ifdef COMPILER_CL - /* Disable some warnings in boost includes */ - #pragma warning(push) - #pragma warning(disable:4244) /* possible loss of data due to data conversion */ - #pragma warning(disable:4458) /* declaration of a variable hides class member */ - - /* The random C++11 library is bugged on MSVC 12 & 14. Use the boost version */ - #include <boost/random.hpp> - #include <boost/random/random_device.hpp> - #define RAN_NAMESPACE boost::random - - #pragma warning(pop) - #pragma warning(push) - #pragma warning(disable:4706) /* Assignment in conditional expression */ -#else - #include <random> - #define RAN_NAMESPACE std -#endif - /* disable some warnings in Random123 includes */ #ifdef COMPILER_GCC #pragma GCC diagnostic push @@ -81,22 +62,6 @@ #include <cstring> #include <limits> -#define SQRT_2_PI 2.50662827463100050240 - -class rng_cxx /* Wrap a SSP random generator into a CXX object */ -{ -public: - FINLINE rng_cxx(struct ssp_rng& rng) : rng(&rng) {} - FINLINE uint64_t operator()() { return ssp_rng_get(rng); } - FINLINE uint64_t min() const { return ssp_rng_min(rng); } - FINLINE uint64_t max() const { return ssp_rng_max(rng); } - - typedef uint64_t result_type; - -private: - ssp_rng* rng; -}; - /******************************************************************************* * KISS PRNG ******************************************************************************/ diff --git a/src/ssp_rng_c.h b/src/ssp_rng_c.h @@ -35,6 +35,27 @@ #include "ssp.h" #include <rsys/ref_count.h> +#define SQRT_2_PI 2.50662827463100050240 + +#ifdef COMPILER_CL + /* Disable some warnings in boost includes */ + #pragma warning(push) + #pragma warning(disable:4244) /* possible loss of data due to data conversion */ + #pragma warning(disable:4458) /* declaration of a variable hides class member */ + + /* The random C++11 library is bugged on MSVC 12 & 14. Use the boost version */ + #include <boost/random.hpp> + #include <boost/random/random_device.hpp> + #define RAN_NAMESPACE boost::random + + #pragma warning(pop) + #pragma warning(push) + #pragma warning(disable:4706) /* Assignment in conditional expression */ +#else + #include <random> + #define RAN_NAMESPACE std +#endif + struct ssp_rng { struct ssp_rng_type type; void* state; @@ -42,5 +63,19 @@ struct ssp_rng { ref_T ref; }; +class rng_cxx /* Wrap a SSP random generator into a CXX object */ +{ +public: + FINLINE rng_cxx(struct ssp_rng& _rng) : rng(_rng) {} + FINLINE uint64_t operator()() { return rng.type.get(rng.state); } + FINLINE uint64_t min() const { return rng.type.min; } + FINLINE uint64_t max() const { return rng.type.max; } + + typedef uint64_t result_type; + +private: + ssp_rng& rng; +}; + #endif /* SSP_RNG_C_H */ diff --git a/src/test_ssp_distrib_gaussian.c b/src/test_ssp_distrib_gaussian.c @@ -0,0 +1,106 @@ +/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) + * + * This software is a program whose purpose is to test the spp library. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#include "ssp.h" +#include "test_ssp_utils.h" +#include <rsys/clock_time.h> + +#define NBS 1000000 + +int +main(int argc, char** argv) +{ + struct ssp_rng_proxy* proxy; + struct ssp_rng* rng; + struct mem_allocator allocator; + struct ssp_distrib_gaussian *gaussian; + int i; + double x = 0, x2 = 0; + double exp_mean = 10, mean; + double exp_std = 3, std; + struct time start, end, res; + char dump[512]; + (void)argc, (void)argv; + + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + + CHECK(ssp_rng_proxy_create(&allocator, &ssp_rng_threefry, 1, &proxy), RES_OK); + CHECK(ssp_rng_proxy_create_rng(proxy, 0, &rng), RES_OK); + + CHECK(ssp_distribution_gaussian_init(&allocator, exp_mean, exp_std, &gaussian), RES_OK); + + time_current(&start); + for (i = 0; i < NBS; i++) { + double _x = ssp_distribution_gaussian_get(gaussian, rng); + x += _x; + x2 += _x * _x; + } + time_current(&end); + time_sub(&res, &end, &start); + time_dump(&res, TIME_SEC|TIME_MSEC|TIME_USEC, NULL, dump, sizeof(dump)); + printf("%s--\n", dump); + + mean = x/NBS; + std = sqrt(x2/NBS - x/NBS*x/NBS); + printf("%g %g\n", mean, std); + CHECK(fabs(mean - exp_mean) < 0.003, 1); + CHECK(fabs(std - exp_std) < 0.001, 1); + + /* same test with inline gaussian generation */ + x = 0; + x2 = 0; + + time_current(&start); + for (i = 0; i < NBS; i++) { + double _x = ssp_ran_gaussian(rng, exp_mean, exp_std); + x += _x; + x2 += _x * _x; + } + time_current(&end); + time_sub(&res, &end, &start); + time_dump(&res, TIME_SEC|TIME_MSEC|TIME_USEC, NULL, dump, sizeof(dump)); + printf("%s--\n", dump); + + mean = x/NBS; + std = sqrt(x2/NBS - x/NBS*x/NBS); + printf("%g %g\n", mean, std); + CHECK(fabs(mean - exp_mean) < 0.003, 1); + CHECK(fabs(std - exp_std) < 0.001, 1); + + CHECK(ssp_distribution_gaussian_ref_put(gaussian), RES_OK); + + CHECK(ssp_rng_ref_put(rng), RES_OK); + CHECK(ssp_rng_proxy_ref_put(proxy), RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHECK(mem_allocated_size(), 0); + return 0; +}