MCKL
Monte Carlo Kernel Library
discrete_distribution.hpp
Go to the documentation of this file.
1 //============================================================================
2 // MCKL/include/mckl/random/discrete_distribution.hpp
3 //----------------------------------------------------------------------------
4 // MCKL: Monte Carlo Kernel Library
5 //----------------------------------------------------------------------------
6 // Copyright (c) 2013-2018, Yan Zhou
7 // All rights reserved.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions are met:
11 //
12 // Redistributions of source code must retain the above copyright notice,
13 // this list of conditions and the following disclaimer.
14 //
15 // Redistributions in binary form must reproduce the above copyright notice,
16 // this list of conditions and the following disclaimer in the documentation
17 // and/or other materials provided with the distribution.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
20 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 // POSSIBILITY OF SUCH DAMAGE.
30 //============================================================================
31 
32 #ifndef MCKL_RANDOM_DISCRETE_DISTRIBUTION_HPP
33 #define MCKL_RANDOM_DISCRETE_DISTRIBUTION_HPP
34 
37 
38 namespace mckl {
39 
42 template <typename IntType>
44 {
46  public:
47  using result_type = IntType;
49 
50  class param_type
51  {
52  public:
53  using result_type = IntType;
55 
56  param_type() = default;
57 
58  template <typename InputIter>
59  param_type(InputIter first, InputIter last) : probability_(first, last)
60  {
61  invariant();
62  }
63 
64  param_type(std::initializer_list<double> weights)
65  : probability_(weights.begin(), weights.end())
66  {
67  invariant();
68  }
69 
70  template <typename UnaryOperation>
71  param_type(std::size_t count, double xmin, double xmax,
72  UnaryOperation unary_op)
73  {
74  probability_.reserve(count);
75  double delta = (xmax - xmin) / static_cast<double>(count);
76  xmin += 0.5 * delta;
77  for (std::size_t i = 0; i != count; ++i) {
78  probability_.push_back(
79  unary_op(xmin + static_cast<double>(i) * delta));
80  }
81  invariant();
82  }
83 
84  Vector<double> probability() const { return probability_; }
85 
86  friend bool operator==(
87  const param_type &param1, const param_type &param2)
88  {
89  return param1.probability_ == param2.probability_;
90  }
91 
92  friend bool operator!=(
93  const param_type &param1, const param_type &param2)
94  {
95  return !(param1 == param2);
96  }
97 
98  template <typename CharT, typename Traits>
99  friend std::basic_ostream<CharT, Traits> &operator<<(
100  std::basic_ostream<CharT, Traits> &os, const param_type &param)
101  {
102  if (!os) {
103  return os;
104  }
105 
106  os << param.probability_;
107 
108  return os;
109  }
110 
111  template <typename CharT, typename Traits>
112  friend std::basic_istream<CharT, Traits> &operator>>(
113  std::basic_istream<CharT, Traits> &is, param_type &param)
114  {
115  if (!is) {
116  return is;
117  }
118 
120  is >> std::ws >> probability;
121 
122  if (is) {
123  double sum = 0;
124  if (is_positive(probability, sum)) {
125  mul(probability.size(), 1 / sum, probability.data(),
126  probability.data());
127  param.probability_ = std::move(probability);
128  } else {
129  is.setstate(std::ios_base::failbit);
130  }
131  }
132 
133  return is;
134  }
135 
136  private:
137  Vector<double> probability_;
138 
139  friend distribution_type;
140 
141  void invariant()
142  {
143  if (probability_.size() == 0) {
144  return;
145  }
146 
147  double sum = 0;
148  bool flag = is_positive(probability_, sum);
149  runtime_assert(flag,
150  "**DiscreteDistribution** constructed with negative weights");
151 
152  mul(probability_.size(), 1 / sum, probability_.data(),
153  probability_.data());
154  }
155 
156  static bool is_positive(const Vector<double> &probability, double &sum)
157  {
158  sum = 0;
159  bool flag = true;
160  for (std::size_t i = 0; i != probability.size(); ++i) {
161  sum += probability[i];
162  if (probability[i] < 0) {
163  flag = false;
164  }
165  }
166 
167  return flag && sum > 0;
168  }
169  }; // class param_type
170 
171  DiscreteDistribution() = default;
172 
173  template <typename InputIter>
174  DiscreteDistribution(InputIter first, InputIter last) : param_(first, last)
175  {
176  }
177 
178  DiscreteDistribution(std::initializer_list<double> weights)
179  : param_(weights)
180  {
181  }
182 
183  template <typename UnaryOperation>
185  std::size_t count, double xmin, double xmax, UnaryOperation &&unary_op)
186  : param_type(count, xmin, xmax, std::forward<UnaryOperation>(unary_op))
187  {
188  }
189 
190  explicit DiscreteDistribution(const param_type &param) : param_(param) {}
191 
193  : param_(std::move(param))
194  {
195  }
196 
197  result_type min() const { return 0; }
198 
199  result_type max() const
200  {
201  return param_.size() == 0 ? 0 : param_.size() - 1;
202  }
203 
204  void reset() {}
205 
206  Vector<double> probability() const { return param_.probability_; }
207 
208  template <typename RNGType>
209  result_type operator()(RNGType &rng) const
210  {
211  return operator()(
212  rng, param_.probability_.begin(), param_.probability_.end(), true);
213  }
214 
233  template <typename RNGType, typename InputIter>
234  result_type operator()(RNGType &rng, InputIter first, InputIter last,
235  bool normalized = false) const
236  {
237  using value_type =
238  typename std::iterator_traits<InputIter>::value_type;
239 
241  value_type u = u01(rng);
242 
243  if (!normalized) {
244  value_type mulw =
245  1 / std::accumulate(first, last, const_zero<value_type>());
246  value_type accw = 0;
247  result_type index = 0;
248  while (first != last) {
249  accw += *first * mulw;
250  if (u <= accw) {
251  return index;
252  }
253  ++first;
254  ++index;
255  }
256 
257  return index - 1;
258  }
259 
260  value_type accw = 0;
261  result_type index = 0;
262  while (first != last) {
263  accw += *first;
264  if (u <= accw) {
265  return index;
266  }
267  ++first;
268  ++index;
269  }
270 
271  return index - 1;
272  }
273 
274  friend bool operator==(
275  const distribution_type &dist1, const distribution_type &dist2)
276  {
277  return dist1.param_ == dist2.param_;
278  }
279 
280  friend bool operator!=(
281  const distribution_type &dist1, const distribution_type &dist2)
282  {
283  return !(dist1 == dist2);
284  }
285 
286  template <typename CharT, typename Traits>
287  friend std::basic_ostream<CharT, Traits> &operator<<(
288  std::basic_ostream<CharT, Traits> &os, const distribution_type &dist)
289  {
290  os << dist.param_;
291 
292  return os;
293  }
294 
295  template <typename CharT, typename Traits>
296  friend std::basic_istream<CharT, Traits> &operator>>(
297  std::basic_istream<CharT, Traits> &is, distribution_type &dist)
298  {
299  is >> std::ws >> dist.param_;
300  if (is) {
301  dist.reset();
302  }
303 
304  return is;
305  }
306 
307  private:
308  param_type param_;
309 }; // class DiscreteDistribution
310 
311 } // namespace mckl
312 
313 #endif // MCKL_RANDOM_DISCRETE_DISTRIBUTION_HPP
DiscreteDistribution(std::size_t count, double xmin, double xmax, UnaryOperation &&unary_op)
void mul(std::size_t n, const float *a, const float *b, float *y)
Definition: vmf.hpp:124
friend bool operator==(const distribution_type &dist1, const distribution_type &dist2)
param_type(InputIter first, InputIter last)
DiscreteDistribution(const param_type &param)
std::vector< T, Alloc > Vector
std::vector with Allocator as the default allocator
Definition: memory.hpp:435
STL namespace.
DiscreteDistribution(param_type &&param)
RealType u01(UIntType u)
Convert uniform unsigned integers to floating points within [0, 1].
Definition: u01.hpp:88
DiscreteDistribution(std::initializer_list< double > weights)
result_type operator()(RNGType &rng) const
friend bool operator!=(const distribution_type &dist1, const distribution_type &dist2)
friend std::basic_istream< CharT, Traits > & operator>>(std::basic_istream< CharT, Traits > &is, distribution_type &dist)
friend bool operator==(const param_type &param1, const param_type &param2)
param_type(std::size_t count, double xmin, double xmax, UnaryOperation unary_op)
Definition: mcmc.hpp:40
friend std::basic_ostream< CharT, Traits > & operator<<(std::basic_ostream< CharT, Traits > &os, const param_type &param)
friend std::basic_ostream< CharT, Traits > & operator<<(std::basic_ostream< CharT, Traits > &os, const distribution_type &dist)
Standard uniform distribution on [0, 1)
DiscreteDistribution(InputIter first, InputIter last)
Draw a single sample given weights.
friend std::basic_istream< CharT, Traits > & operator>>(std::basic_istream< CharT, Traits > &is, param_type &param)
param_type(std::initializer_list< double > weights)
Vector< double > probability() const
result_type operator()(RNGType &rng, InputIter first, InputIter last, bool normalized=false) const
Draw sample with external probabilities.
DiscreteDistribution< IntType > distribution_type
friend bool operator!=(const param_type &param1, const param_type &param2)
#define MCKL_DEFINE_RANDOM_DISTRIBUTION_ASSERT_INT_TYPE(Name, MinBits)
void runtime_assert(bool cond, const char *msg, bool soft=false)
Definition: assert.hpp:65