161 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			161 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| //---------------------------------------------------------------------------//
 | |
| // 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
 | 
