From 1799ee1b6e4e386797fdc0e9b2c52c84a8cc0969 Mon Sep 17 00:00:00 2001 From: Jakub Szuppe Date: Tue, 10 May 2016 22:04:23 +0200 Subject: [PATCH 1/5] Support for first==last in discrete_distribution range ctor Add support for the case with first iterator being equal to last iterator in range ctor. Now it behaves like ctor in std::discrete_distribution. Additionaly: now method probabilities() returns correct vector of probabilities. --- .../compute/random/discrete_distribution.hpp | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/include/boost/compute/random/discrete_distribution.hpp b/include/boost/compute/random/discrete_distribution.hpp index 03602176d..648432459 100644 --- a/include/boost/compute/random/discrete_distribution.hpp +++ b/include/boost/compute/random/discrete_distribution.hpp @@ -11,6 +11,9 @@ #ifndef BOOST_COMPUTE_RANDOM_DISCRETE_DISTRIBUTION_HPP #define BOOST_COMPUTE_RANDOM_DISCRETE_DISTRIBUTION_HPP +#include + +#include #include #include @@ -42,27 +45,32 @@ class discrete_distribution typedef IntType result_type; /// Creates a new discrete distribution with weights given by - /// the range [\p first, \p last) + /// the range [\p first, \p last). template discrete_distribution(InputIterator first, InputIterator last) - : m_n((std::max)(size_t(1), static_cast(std::distance(first, last)))), - m_probabilities((std::max)(size_t(1), static_cast(std::distance(first, last)))) + : m_probabilities(first, last), + m_scanned_probabilities(std::distance(first, last)) { - double sum = 0; - - for(InputIterator iter = first; iter!=last; iter++) - { - sum += *iter; + if(first != last) { + // after this m_scanned_probabilities.back() is a sum of all + // weights from the range [first, last) + std::partial_sum(first, last, m_scanned_probabilities.begin()); + + std::vector::iterator i = m_probabilities.begin(); + std::vector::iterator j = m_scanned_probabilities.begin(); + for(; i != m_probabilities.end(); ++i, ++j) + { + // dividing each weight by sum of all weights to + // get probabilities + *i = *i / m_scanned_probabilities.back(); + // dividing each partial sum of weights by sum of + // all weights to get partial sums of probabilities + *j = *j / m_scanned_probabilities.back(); + } } - - InputIterator iter = first; - m_probabilities[0] = (*iter)/sum; - iter++; - - for(size_t i = 1; i < m_n; i++) - { - m_probabilities[i] = m_probabilities[i-1] + (*iter)/sum; - iter++; + else { + m_probabilities.push_back(double(1)); + m_scanned_probabilities.push_back(double(1)); } } @@ -71,19 +79,13 @@ class discrete_distribution { } - /// Returns the value of n - result_type n() const - { - return m_n; - } - /// Returns the probabilities ::std::vector probabilities() const { return m_probabilities; } - /// Generates uniformily distributed integers and stores + /// Generates uniformly distributed integers and stores /// them to the range [\p first, \p last). template void generate(OutputIterator first, @@ -96,15 +98,15 @@ class discrete_distribution source = source + "{\n" + "float rno = convert_float(x) / UINT_MAX;\n"; - for(size_t i=0; i m_probabilities; + ::std::vector m_scanned_probabilities; BOOST_STATIC_ASSERT_MSG( boost::is_integral::value, From e9b2c0ff4b3997b5580df986a88957d674ed9883 Mon Sep 17 00:00:00 2001 From: Jakub Szuppe Date: Tue, 10 May 2016 22:14:18 +0200 Subject: [PATCH 2/5] Add default constructor for discrete_distribution --- include/boost/compute/random/discrete_distribution.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/boost/compute/random/discrete_distribution.hpp b/include/boost/compute/random/discrete_distribution.hpp index 648432459..a40fabf7e 100644 --- a/include/boost/compute/random/discrete_distribution.hpp +++ b/include/boost/compute/random/discrete_distribution.hpp @@ -44,6 +44,15 @@ class discrete_distribution public: typedef IntType result_type; + /// Creates a new discrete distribution with a single weight p = { 1 }. + /// This distribution produces only zeroes. + discrete_distribution() + : m_probabilities(1, double(1)), + m_scanned_probabilities(1, double(1)) + { + + } + /// Creates a new discrete distribution with weights given by /// the range [\p first, \p last). template From 227c05cbdc4ffdb5a7b7483e1d29f16e5396fb34 Mon Sep 17 00:00:00 2001 From: Jakub Szuppe Date: Tue, 10 May 2016 22:14:43 +0200 Subject: [PATCH 3/5] Add min() and max() methods for discrete_distribution --- .../compute/random/discrete_distribution.hpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/include/boost/compute/random/discrete_distribution.hpp b/include/boost/compute/random/discrete_distribution.hpp index a40fabf7e..17bdfa038 100644 --- a/include/boost/compute/random/discrete_distribution.hpp +++ b/include/boost/compute/random/discrete_distribution.hpp @@ -94,6 +94,24 @@ class discrete_distribution return m_probabilities; } + /// Returns the minimum potentially generated value. + result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const + { + return result_type(0); + } + + /// Returns the maximum potentially generated value. + result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const + { + size_t type_max = static_cast( + (std::numeric_limits::max)() + ); + if(m_probabilities.size() - 1 > type_max) { + return (std::numeric_limits::max)(); + } + return static_cast(m_probabilities.size() - 1); + } + /// Generates uniformly distributed integers and stores /// them to the range [\p first, \p last). template From 650e828542774fa978c6643c4995bf8999ebfc4f Mon Sep 17 00:00:00 2001 From: Jakub Szuppe Date: Tue, 10 May 2016 22:16:54 +0200 Subject: [PATCH 4/5] Add more tests for discrete_distribution --- test/test_discrete_distribution.cpp | 171 ++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) diff --git a/test/test_discrete_distribution.cpp b/test/test_discrete_distribution.cpp index 3f2e5aa4b..f09a3f9a2 100644 --- a/test/test_discrete_distribution.cpp +++ b/test/test_discrete_distribution.cpp @@ -11,6 +11,8 @@ #define BOOST_TEST_MODULE TestDiscreteDistribution #include +#include + #include #include #include @@ -51,4 +53,173 @@ distribution.generate(vec.begin(), vec.end(), engine, queue); ); } +BOOST_AUTO_TEST_CASE(discrete_distribution) +{ + using boost::compute::uint_; + using boost::compute::lambda::_1; + + size_t size = 100; + boost::compute::vector vec(size, context); + + // initialize the default random engine + boost::compute::default_random_engine engine(queue); + + // initialize weights + int weights[] = {10, 40, 40, 10}; + + // setup the discrete distribution + boost::compute::discrete_distribution distribution( + weights, weights + 4 + ); + + std::vector p = distribution.probabilities(); + BOOST_CHECK_CLOSE(p[0], double(0.1), 0.001); + BOOST_CHECK_CLOSE(p[1], double(0.4), 0.001); + BOOST_CHECK_CLOSE(p[2], double(0.4), 0.001); + BOOST_CHECK_CLOSE(p[3], double(0.1), 0.001); + + BOOST_CHECK_EQUAL((distribution.min)(), uint_(0)); + BOOST_CHECK_EQUAL((distribution.max)(), uint_(3)); + + // generate the random values and store them to 'vec' + distribution.generate(vec.begin(), vec.end(), engine, queue); + + BOOST_CHECK_EQUAL( + boost::compute::count_if( + vec.begin(), vec.end(), _1 < 4, queue + ), + size + ); +} + +BOOST_AUTO_TEST_CASE(discrete_distribution_default_ctor) +{ + using boost::compute::uint_; + using boost::compute::lambda::_1; + + size_t size = 100; + boost::compute::vector vec(size, context); + + // initialize the default random engine + boost::compute::default_random_engine engine(queue); + + // call default constructor + boost::compute::discrete_distribution distribution; + + std::vector p = distribution.probabilities(); + BOOST_CHECK_CLOSE(p[0], double(1), 0.001); + + // generate the random values and store them to 'vec' + distribution.generate(vec.begin(), vec.end(), engine, queue); + + BOOST_CHECK_EQUAL( + boost::compute::count_if( + vec.begin(), vec.end(), _1 == 0, queue + ), + size + ); +} + +BOOST_AUTO_TEST_CASE(discrete_distribution_one_weight) +{ + using boost::compute::uint_; + using boost::compute::lambda::_1; + + size_t size = 100; + boost::compute::vector vec(size, context); + + // initialize the default random engine + boost::compute::default_random_engine engine(queue); + + std::vector weights(1, 1); + // call default constructor + boost::compute::discrete_distribution distribution( + weights.begin(), weights.end() + ); + + std::vector p = distribution.probabilities(); + BOOST_CHECK_CLOSE(p[0], double(1), 0.001); + + BOOST_CHECK_EQUAL((distribution.min)(), uint_(0)); + BOOST_CHECK_EQUAL((distribution.max)(), uint_(0)); + + // generate the random values and store them to 'vec' + distribution.generate(vec.begin(), vec.end(), engine, queue); + + BOOST_CHECK_EQUAL( + boost::compute::count_if( + vec.begin(), vec.end(), _1 == 0, queue + ), + size + ); +} + +BOOST_AUTO_TEST_CASE(discrete_distribution_empty_weights) +{ + using boost::compute::uint_; + using boost::compute::lambda::_1; + + size_t size = 100; + boost::compute::vector vec(size, context); + + // initialize the default random engine + boost::compute::default_random_engine engine(queue); + + std::vector weights; + // weights.begin() == weights.end() + boost::compute::discrete_distribution distribution( + weights.begin(), weights.end() + ); + + std::vector p = distribution.probabilities(); + BOOST_CHECK_CLOSE(p[0], double(1), 0.001); + + BOOST_CHECK_EQUAL((distribution.min)(), uint_(0)); + BOOST_CHECK_EQUAL((distribution.max)(), uint_(0)); + + // generate the random values and store them to 'vec' + distribution.generate(vec.begin(), vec.end(), engine, queue); + + BOOST_CHECK_EQUAL( + boost::compute::count_if( + vec.begin(), vec.end(), _1 == 0, queue + ), + size + ); +} + +BOOST_AUTO_TEST_CASE(discrete_distribution_uchar) +{ + using boost::compute::uchar_; + using boost::compute::lambda::_1; + + size_t size = 100; + boost::compute::vector vec(size, context); + + // initialize the default random engine + boost::compute::default_random_engine engine(queue); + + // initialize weights + std::vector weights(258, 0); + weights[257] = 1; + + // setup the discrete distribution + boost::compute::discrete_distribution distribution( + weights.begin(), weights.end() + ); + + BOOST_CHECK_EQUAL((distribution.min)(), uchar_(0)); + BOOST_CHECK_EQUAL((distribution.max)(), uchar_(255)); + + // generate the random values and store them to 'vec' + distribution.generate(vec.begin(), vec.end(), engine, queue); + + BOOST_CHECK_EQUAL( + boost::compute::count_if( + vec.begin(), vec.end(), _1 == uchar_(1), queue + ), + size + ); +} + BOOST_AUTO_TEST_SUITE_END() From 56ad192b92ad227603ff158a77e7d07320d6c91f Mon Sep 17 00:00:00 2001 From: Jakub Szuppe Date: Tue, 10 May 2016 22:28:25 +0200 Subject: [PATCH 5/5] Now discrete_distribution generates IntType values Class discrete_distribution should generate values of type IntType. Until now it always generated unsigned int values. --- .../compute/random/discrete_distribution.hpp | 3 ++- test/test_discrete_distribution.cpp | 20 +++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/include/boost/compute/random/discrete_distribution.hpp b/include/boost/compute/random/discrete_distribution.hpp index 17bdfa038..86249538a 100644 --- a/include/boost/compute/random/discrete_distribution.hpp +++ b/include/boost/compute/random/discrete_distribution.hpp @@ -120,7 +120,7 @@ class discrete_distribution Generator &generator, command_queue &queue) { - std::string source = "inline uint scale_random(uint x)\n"; + std::string source = "inline IntType scale_random(uint x)\n"; source = source + "{\n" + @@ -139,6 +139,7 @@ class discrete_distribution BOOST_COMPUTE_FUNCTION(IntType, scale_random, (const uint_ x), {}); scale_random.set_source(source); + scale_random.define("IntType", type_name()); generator.generate(first, last, scale_random, queue); } diff --git a/test/test_discrete_distribution.cpp b/test/test_discrete_distribution.cpp index f09a3f9a2..6c4b8d713 100644 --- a/test/test_discrete_distribution.cpp +++ b/test/test_discrete_distribution.cpp @@ -191,10 +191,12 @@ BOOST_AUTO_TEST_CASE(discrete_distribution_empty_weights) BOOST_AUTO_TEST_CASE(discrete_distribution_uchar) { using boost::compute::uchar_; + using boost::compute::uint_; using boost::compute::lambda::_1; size_t size = 100; - boost::compute::vector vec(size, context); + boost::compute::vector uchar_vec(size, context); + boost::compute::vector uint_vec(size, context); // initialize the default random engine boost::compute::default_random_engine engine(queue); @@ -211,12 +213,22 @@ BOOST_AUTO_TEST_CASE(discrete_distribution_uchar) BOOST_CHECK_EQUAL((distribution.min)(), uchar_(0)); BOOST_CHECK_EQUAL((distribution.max)(), uchar_(255)); - // generate the random values and store them to 'vec' - distribution.generate(vec.begin(), vec.end(), engine, queue); + // generate the random uchar_ values to the uchar_ vector + distribution.generate(uchar_vec.begin(), uchar_vec.end(), engine, queue); + + BOOST_CHECK_EQUAL( + boost::compute::count_if( + uchar_vec.begin(), uchar_vec.end(), _1 == uchar_(1), queue + ), + size + ); + + // generate the random uchar_ values to the uint_ vector + distribution.generate(uint_vec.begin(), uint_vec.end(), engine, queue); BOOST_CHECK_EQUAL( boost::compute::count_if( - vec.begin(), vec.end(), _1 == uchar_(1), queue + uint_vec.begin(), uint_vec.end(), _1 == uint_(1), queue ), size );