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 |