161 lines
5.4 KiB
C++
161 lines
5.4 KiB
C++
|
//---------------------------------------------------------------------------//
|
||
|
// Copyright (c) 2014 Roshan <thisisroshansmail@gmail.com>
|
||
|
//
|
||
|
// Distributed under the Boost Software License, Version 1.0
|
||
|
// See accompanying file LICENSE_1_0.txt or copy at
|
||
|
// http://www.boost.org/LICENSE_1_0.txt
|
||
|
//
|
||
|
// See http://boostorg.github.com/compute for more information.
|
||
|
//---------------------------------------------------------------------------//
|
||
|
|
||
|
#ifndef BOOST_COMPUTE_RANDOM_DISCRETE_DISTRIBUTION_HPP
|
||
|
#define BOOST_COMPUTE_RANDOM_DISCRETE_DISTRIBUTION_HPP
|
||
|
|
||
|
#include <numeric>
|
||
|
|
||
|
#include <boost/config.hpp>
|
||
|
#include <boost/type_traits.hpp>
|
||
|
#include <boost/static_assert.hpp>
|
||
|
|
||
|
#include <boost/compute/command_queue.hpp>
|
||
|
#include <boost/compute/function.hpp>
|
||
|
#include <boost/compute/algorithm/accumulate.hpp>
|
||
|
#include <boost/compute/algorithm/copy.hpp>
|
||
|
#include <boost/compute/algorithm/transform.hpp>
|
||
|
#include <boost/compute/detail/literal.hpp>
|
||
|
#include <boost/compute/types/fundamental.hpp>
|
||
|
|
||
|
namespace boost {
|
||
|
namespace compute {
|
||
|
|
||
|
/// \class discrete_distribution
|
||
|
/// \brief Produces random integers on the interval [0, n), where
|
||
|
/// probability of each integer is given by the weight of the ith
|
||
|
/// integer divided by the sum of all weights.
|
||
|
///
|
||
|
/// The following example shows how to setup a discrete distribution to
|
||
|
/// produce 0 and 1 with equal probability
|
||
|
///
|
||
|
/// \snippet test/test_discrete_distribution.cpp generate
|
||
|
///
|
||
|
template<class IntType = uint_>
|
||
|
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<class InputIterator>
|
||
|
discrete_distribution(InputIterator first, InputIterator last)
|
||
|
: m_probabilities(first, last),
|
||
|
m_scanned_probabilities(std::distance(first, last))
|
||
|
{
|
||
|
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<double>::iterator i = m_probabilities.begin();
|
||
|
std::vector<double>::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();
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
m_probabilities.push_back(double(1));
|
||
|
m_scanned_probabilities.push_back(double(1));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Destroys the discrete_distribution object.
|
||
|
~discrete_distribution()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/// Returns the probabilities
|
||
|
::std::vector<double> probabilities() const
|
||
|
{
|
||
|
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<size_t>(
|
||
|
(std::numeric_limits<result_type>::max)()
|
||
|
);
|
||
|
if(m_probabilities.size() - 1 > type_max) {
|
||
|
return (std::numeric_limits<result_type>::max)();
|
||
|
}
|
||
|
return static_cast<result_type>(m_probabilities.size() - 1);
|
||
|
}
|
||
|
|
||
|
/// Generates uniformly distributed integers and stores
|
||
|
/// them to the range [\p first, \p last).
|
||
|
template<class OutputIterator, class Generator>
|
||
|
void generate(OutputIterator first,
|
||
|
OutputIterator last,
|
||
|
Generator &generator,
|
||
|
command_queue &queue)
|
||
|
{
|
||
|
std::string source = "inline IntType scale_random(uint x)\n";
|
||
|
|
||
|
source = source +
|
||
|
"{\n" +
|
||
|
"float rno = convert_float(x) / UINT_MAX;\n";
|
||
|
for(size_t i = 0; i < m_scanned_probabilities.size() - 1; i++)
|
||
|
{
|
||
|
source = source +
|
||
|
"if(rno <= " + detail::make_literal<float>(m_scanned_probabilities[i]) + ")\n" +
|
||
|
" return " + detail::make_literal(i) + ";\n";
|
||
|
}
|
||
|
|
||
|
source = source +
|
||
|
"return " + detail::make_literal(m_scanned_probabilities.size() - 1) + ";\n" +
|
||
|
"}\n";
|
||
|
|
||
|
BOOST_COMPUTE_FUNCTION(IntType, scale_random, (const uint_ x), {});
|
||
|
|
||
|
scale_random.set_source(source);
|
||
|
scale_random.define("IntType", type_name<IntType>());
|
||
|
|
||
|
generator.generate(first, last, scale_random, queue);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
::std::vector<double> m_probabilities;
|
||
|
::std::vector<double> m_scanned_probabilities;
|
||
|
|
||
|
BOOST_STATIC_ASSERT_MSG(
|
||
|
boost::is_integral<IntType>::value,
|
||
|
"Template argument must be integral"
|
||
|
);
|
||
|
};
|
||
|
|
||
|
} // end compute namespace
|
||
|
} // end boost namespace
|
||
|
|
||
|
#endif // BOOST_COMPUTE_RANDOM_UNIFORM_INT_DISTRIBUTION_HPP
|