vSMC
vSMC: Scalable Monte Carlo
counter.hpp
Go to the documentation of this file.
1 //============================================================================
2 // vSMC/include/vsmc/rng/counter.hpp
3 //----------------------------------------------------------------------------
4 // vSMC: Scalable Monte Carlo
5 //----------------------------------------------------------------------------
6 // Copyright (c) 2013-2015, 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 VSMC_RNG_COUNTER_HPP
33 #define VSMC_RNG_COUNTER_HPP
34 
36 
37 namespace vsmc
38 {
39 
40 namespace internal
41 {
42 
43 template <std::size_t, typename T, std::size_t K>
44 inline void increment_single(std::array<T, K> &, std::false_type)
45 {
46 }
47 
48 template <std::size_t N, typename T, std::size_t K>
49 inline void increment_single(std::array<T, K> &ctr, std::true_type)
50 {
51  if (++std::get<N>(ctr) != 0)
52  return;
53 
54  increment_single<N + 1>(ctr, std::integral_constant<bool, N + 1 < K>());
55 }
56 
57 } // namespace vsmc::internal
58 
61 template <typename T, std::size_t K>
62 inline void increment(std::array<T, K> &ctr)
63 {
64  internal::increment_single<0>(ctr, std::true_type());
65 }
66 
69 template <typename T, std::size_t K, T NSkip>
70 inline void increment(std::array<T, K> &ctr, std::integral_constant<T, NSkip>)
71 {
72  if (ctr.front() < std::numeric_limits<T>::max VSMC_MNE() - NSkip) {
73  ctr.front() += NSkip;
74  } else {
75  ctr.front() += NSkip;
76  internal::increment_single<1>(
77  ctr, std::integral_constant<bool, 1 < K>());
78  }
79 }
80 
83 template <typename T, std::size_t K>
84 inline void increment(std::array<T, K> &ctr, T nskip)
85 {
86  if (ctr.front() < std::numeric_limits<T>::max VSMC_MNE() - nskip) {
87  ctr.front() += nskip;
88  } else {
89  ctr.front() += nskip;
90  internal::increment_single<1>(
91  ctr, std::integral_constant<bool, 1 < K>());
92  }
93 }
94 
95 namespace internal
96 {
97 
98 template <std::size_t, typename T, std::size_t K, std::size_t Blocks>
99 inline void increment_block_set(const std::array<T, K> &,
100  std::array<std::array<T, K>, Blocks> &, std::false_type)
101 {
102 }
103 
104 template <std::size_t B, typename T, std::size_t K, std::size_t Blocks>
105 inline void increment_block_set(const std::array<T, K> &ctr,
106  std::array<std::array<T, K>, Blocks> &ctr_block, std::true_type)
107 {
108  std::get<B>(ctr_block) = ctr;
109  increment_block_set<B + 1>(
110  ctr, ctr_block, std::integral_constant<bool, B + 1 < Blocks>());
111 }
112 
113 template <std::size_t, typename T, std::size_t K, std::size_t Blocks>
114 inline void increment_block(std::array<T, K> &,
115  std::array<std::array<T, K>, Blocks> &, std::false_type)
116 {
117 }
118 
119 template <std::size_t B, typename T, std::size_t K, std::size_t Blocks>
120 inline void increment_block(std::array<T, K> &ctr,
121  std::array<std::array<T, K>, Blocks> &ctr_block, std::true_type)
122 {
123  increment(std::get<B>(ctr_block), std::integral_constant<T, B + 1>());
124  increment_block<B + 1>(
125  ctr, ctr_block, std::integral_constant<bool, B + 1 < Blocks>());
126 }
127 
128 template <std::size_t, typename T, std::size_t K, std::size_t Blocks>
129 inline void increment_block_safe(std::array<T, K> &,
130  std::array<std::array<T, K>, Blocks> &, std::false_type)
131 {
132 }
133 
134 template <std::size_t B, typename T, std::size_t K, std::size_t Blocks>
135 inline void increment_block_safe(std::array<T, K> &ctr,
136  std::array<std::array<T, K>, Blocks> &ctr_block, std::true_type)
137 {
138  std::get<B>(ctr_block).front() += B + 1;
139  increment_block_safe<B + 1>(
140  ctr, ctr_block, std::integral_constant<bool, B + 1 < Blocks>());
141 }
142 
143 } // namespace vsmc::internal
144 
148 template <typename T, std::size_t K, std::size_t Blocks>
149 inline void increment(
150  std::array<T, K> &ctr, std::array<std::array<T, K>, Blocks> &ctr_block)
151 {
152  internal::increment_block_set<0>(
153  ctr, ctr_block, std::integral_constant<bool, 0 < Blocks>());
154  if (ctr.front() <
155  std::numeric_limits<T>::max VSMC_MNE() - static_cast<T>(Blocks)) {
156  internal::increment_block_safe<0>(
157  ctr, ctr_block, std::integral_constant<bool, 0 < Blocks>());
158  } else {
159  internal::increment_block<0>(
160  ctr, ctr_block, std::integral_constant<bool, 0 < Blocks>());
161  }
162  ctr = ctr_block.back();
163 }
164 
165 namespace internal
166 {
167 
168 template <typename T, std::size_t K>
169 inline void increment_block_set(const std::array<T, K> &ctr, std::size_t n,
170  std::array<T, K> *ctr_block, std::false_type)
171 {
172  for (std::size_t i = 0; i != n; ++i)
173  ctr_block[i] = ctr;
174 }
175 
176 #if VSMC_HAS_AVX2
177 
178 template <typename T, std::size_t K>
179 inline void increment_block_set(const std::array<T, K> &ctr, std::size_t n,
180  std::array<T, K> *ctr_block, std::true_type)
181 {
182  const std::size_t Blocks = M256I<T>::size() / K;
183  const std::size_t m = n / Blocks;
184  const std::size_t l = n % Blocks;
185  M256I<> c;
186  std::array<std::array<T, K>, Blocks> cb;
187  increment_block_set<0>(ctr, cb, std::true_type());
188  c.load(cb.data());
189  if (reinterpret_cast<std::uintptr_t>(ctr_block) % 32 == 0) {
190  for (std::size_t i = 0; i != m; ++i, ctr_block += Blocks)
191  c.store_a(ctr_block);
192  } else {
193  for (std::size_t i = 0; i != m; ++i, ctr_block += Blocks)
194  c.store_u(ctr_block);
195  }
196  for (std::size_t i = 0; i != l; ++i)
197  ctr_block[i] = ctr;
198 }
199 
200 template <typename T, std::size_t K>
201 inline void increment_block_set(
202  const std::array<T, K> &ctr, std::size_t n, std::array<T, K> *ctr_block)
203 {
204  increment_block_set(ctr, n, ctr_block,
205  std::integral_constant<bool, M256I<T>::size() % K == 0>());
206 }
207 
208 #elif VSMC_HAS_SSE2
209 
210 template <typename T, std::size_t K>
211 inline void increment_block_set(const std::array<T, K> &ctr, std::size_t n,
212  std::array<T, K> *ctr_block, std::true_type)
213 {
214  const std::size_t Blocks = M128I<T>::size() / K;
215  const std::size_t m = n / Blocks;
216  const std::size_t l = n % Blocks;
217  M128I<> c;
218  std::array<std::array<T, K>, Blocks> cb;
219  increment_block_set<0>(ctr, cb, std::true_type());
220  c.load(cb.data());
221  if (reinterpret_cast<std::uintptr_t>(ctr_block) % 16 == 0) {
222  for (std::size_t i = 0; i != m; ++i, ctr_block += Blocks)
223  c.store_a(ctr_block);
224  } else {
225  for (std::size_t i = 0; i != m; ++i, ctr_block += Blocks)
226  c.store_u(ctr_block);
227  }
228  for (std::size_t i = 0; i != l; ++i)
229  ctr_block[i] = ctr;
230 }
231 
232 template <typename T, std::size_t K>
233 inline void increment_block_set(
234  const std::array<T, K> &ctr, std::size_t n, std::array<T, K> *ctr_block)
235 {
236  increment_block_set(ctr, n, ctr_block,
237  std::integral_constant<bool, M128I<T>::size() % K == 0>());
238 }
239 
240 #else // VSMC_HAS_SSE2
241 
242 template <typename T, std::size_t K>
244  const std::array<T, K> &ctr, std::size_t n, std::array<T, K> *ctr_block)
245 {
246  increment_block_set(ctr, n, ctr_block, std::false_type());
247 }
248 
249 #endif // VSMC_HAS_SSE2
250 
251 } // namespace vsmc::internal
252 
256 template <typename T, std::size_t K>
257 inline void increment(
258  std::array<T, K> &ctr, std::size_t n, std::array<T, K> *ctr_block)
259 {
260  if (n == 0)
261  return;
262 
263  increment(ctr);
264  const std::uint64_t m =
265  static_cast<std::uint64_t>(std::numeric_limits<T>::max VSMC_MNE());
266  const std::uint64_t l = static_cast<std::uint64_t>(ctr.front());
267  const std::uint64_t k = static_cast<std::uint64_t>(n);
268  if (k < m && l < m - k) {
269  internal::increment_block_set(ctr, n, ctr_block);
270  const T p = static_cast<T>(n);
271  for (T i = 0; i != p; ++i)
272  ctr_block[i].front() += i;
273  } else if (k < m) {
274  internal::increment_block_set(ctr, n, ctr_block);
275  const T p = static_cast<T>(n);
276  for (T i = 0; i != p; ++i)
277  increment(ctr_block[i], i);
278  } else {
279  for (std::size_t i = 0; i != n; ++i) {
280  ctr_block[i] = ctr;
281  increment(ctr);
282  }
283  }
284  ctr = ctr_block[n - 1];
285 }
286 
289 template <typename Generator>
291 {
292  public:
293  using result_type = typename Generator::result_type;
294  using ctr_type = typename Generator::ctr_type;
295  using key_type = typename Generator::key_type;
296 
297  explicit CounterEngine(result_type s = 0) : index_(M_) { seed(s); }
298 
299  template <typename SeedSeq>
300  explicit CounterEngine(SeedSeq &seq,
301  typename std::enable_if<internal::is_seed_seq<SeedSeq, result_type,
302  key_type, CounterEngine<Generator>>::value>::type * = nullptr)
303  : index_(M_)
304  {
305  seed(seq);
306  }
307 
308  explicit CounterEngine(const key_type &k) : index_(M_) { seed(k); }
309 
311  {
312  key_.fill(0);
313  key_.front() = s;
314  reset();
315  }
316 
317  template <typename SeedSeq>
318  void seed(
319  SeedSeq &seq, typename std::enable_if<internal::is_seed_seq<SeedSeq,
320  result_type, key_type>::value>::type * = nullptr)
321  {
322  seq.generator(key_.begin(), key_.end());
323  reset();
324  }
325 
326  void seed(const key_type &k)
327  {
328  key_ = k;
329  reset();
330  }
331 
332  const ctr_type &ctr() const { return ctr_; }
333 
334  const key_type &key() const { return key_; }
335 
336  void ctr(const ctr_type &c)
337  {
338  ctr_ = c;
339  index_ = M_;
340  }
341 
342  void key(const key_type &k)
343  {
344  key_ = k;
345  reset();
346  }
347 
349  {
350  if (index_ == M_) {
351  generator_(ctr_, key_, buffer_);
352  index_ = 0;
353  }
354 
355  return buffer_[index_++];
356  }
357 
358  void operator()(std::size_t n, result_type *r)
359  {
360  if (n * sizeof(result_type) <= 32) {
361  for (std::size_t i = 0; i != n; ++i)
362  r[i] = operator()();
363  return;
364  }
365 
366  std::size_t p = 32 -
367  static_cast<std::size_t>(reinterpret_cast<std::uintptr_t>(r) % 32);
368  if (p % sizeof(result_type) == 0) {
369  p /= sizeof(result_type);
370  for (std::size_t i = 0; i != p; ++i)
371  r[i] = operator()();
372  n -= p;
373  r += p;
374  }
375 
376  const std::size_t q = generator_(ctr_, key_, n, r);
377  n -= q;
378  r += q;
379 
380  const std::size_t m = n / M_;
381  std::array<result_type, M_> *s =
382  reinterpret_cast<std::array<result_type, M_> *>(r);
383  for (std::size_t i = 0; i != m; ++i)
384  generator_(ctr_, key_, s[i]);
385  n -= m * M_;
386  r += m * M_;
387 
388  for (std::size_t i = 0; i != n; ++i)
389  r[i] = operator()();
390  }
391 
392  void discard(result_type nskip)
393  {
394  std::size_t n = static_cast<std::size_t>(nskip);
395  if (index_ + n <= M_) {
396  index_ += n;
397  return;
398  }
399 
400  n -= M_ - index_;
401  if (n <= M_) {
402  index_ = M_;
403  operator()();
404  index_ = n;
405  return;
406  }
407 
408  increment(ctr_, static_cast<result_type>(n / M_));
409  index_ = M_;
410  operator()();
411  index_ = n % M_;
412  }
413 
414  static constexpr result_type min VSMC_MNE()
415  {
416  return std::numeric_limits<result_type>::min VSMC_MNE();
417  }
418 
419  static constexpr result_type max VSMC_MNE()
420  {
421  return std::numeric_limits<result_type>::max VSMC_MNE();
422  }
423 
424  friend bool operator==(const CounterEngine<Generator> &eng1,
425  const CounterEngine<Generator> &eng2)
426  {
427  if (eng1.buffer_ != eng2.buffer_)
428  return false;
429  if (eng1.ctr_ != eng2.ctr_)
430  return false;
431  if (eng1.key_ != eng2.key_)
432  return false;
433  if (eng1.index_ != eng2.index_)
434  return false;
435  return true;
436  }
437 
438  friend bool operator!=(const CounterEngine<Generator> &eng1,
439  const CounterEngine<Generator> &eng2)
440  {
441  return !(eng1 == eng2);
442  }
443 
444  template <typename CharT, typename Traits>
445  friend std::basic_ostream<CharT, Traits> &operator<<(
446  std::basic_ostream<CharT, Traits> &os,
447  const CounterEngine<Generator> &eng)
448  {
449  if (!os.good())
450  return os;
451 
452  os << eng.buffer_ << ' ';
453  os << eng.ctr_ << ' ';
454  os << eng.key_ << ' ';
455  os << eng.index_;
456 
457  return os;
458  }
459 
460  template <typename CharT, typename Traits>
461  friend std::basic_istream<CharT, Traits> &operator>>(
462  std::basic_istream<CharT, Traits> &is, CounterEngine<Generator> &eng)
463  {
464  if (!is.good())
465  return is;
466 
467  CounterEngine<Generator> eng_tmp;
468  is >> std::ws >> eng_tmp.buffer_;
469  is >> std::ws >> eng_tmp.ctr_;
470  is >> std::ws >> eng_tmp.key_;
471  is >> std::ws >> eng_tmp.index_;
472 
473  if (is.good()) {
474  eng_tmp.generator_.reset(eng_tmp.key_);
475  eng = std::move(eng_tmp);
476  }
477 
478  return is;
479  }
480 
481  private:
482  static constexpr std::size_t M_ = Generator::size();
483 
484  std::array<result_type, M_> buffer_;
485  ctr_type ctr_;
486  key_type key_;
487  Generator generator_;
488  std::size_t index_;
489 
490  void reset()
491  {
492  ctr_.fill(0);
493  generator_.reset(key_);
494  index_ = M_;
495  }
496 }; // class CounterEngine
497 
498 template <typename Generator>
499 inline void rng_rand(CounterEngine<Generator> &rng, std::size_t n,
501 {
502  rng(n, r);
503 }
504 
505 } // namespace vsmc
506 
507 #endif // VSMC_RNG_COUNTER_HPP
Definition: monitor.hpp:49
const ctr_type & ctr() const
Definition: counter.hpp:332
const key_type & key() const
Definition: counter.hpp:334
friend bool operator==(const CounterEngine< Generator > &eng1, const CounterEngine< Generator > &eng2)
Definition: counter.hpp:424
result_type operator()()
Definition: counter.hpp:348
void increment(std::array< T, K > &ctr)
Increment a counter by one.
Definition: counter.hpp:62
typename Generator::key_type key_type
Definition: counter.hpp:295
ulong uint64_t
Definition: opencl.h:40
typename Generator::ctr_type ctr_type
Definition: counter.hpp:294
void increment_block(std::array< T, K > &, std::array< std::array< T, K >, Blocks > &, std::false_type)
Definition: counter.hpp:114
Counter based RNG engine.
Definition: counter.hpp:290
CounterEngine(SeedSeq &seq, typename std::enable_if< internal::is_seed_seq< SeedSeq, result_type, key_type, CounterEngine< Generator >>::value >::type *=nullptr)
Definition: counter.hpp:300
void rng_rand(RNGType &rng, BernoulliDistribution< IntType > &dist, std::size_t n, IntType *r)
void increment_block_set(const std::array< T, K > &, std::array< std::array< T, K >, Blocks > &, std::false_type)
Definition: counter.hpp:99
void operator()(std::size_t n, result_type *r)
Definition: counter.hpp:358
CounterEngine(result_type s=0)
Definition: counter.hpp:297
void key(const key_type &k)
Definition: counter.hpp:342
#define VSMC_MNE
Definition: defines.hpp:38
friend std::basic_ostream< CharT, Traits > & operator<<(std::basic_ostream< CharT, Traits > &os, const CounterEngine< Generator > &eng)
Definition: counter.hpp:445
typename Generator::result_type result_type
Definition: counter.hpp:293
void ctr(const ctr_type &c)
Definition: counter.hpp:336
void seed(SeedSeq &seq, typename std::enable_if< internal::is_seed_seq< SeedSeq, result_type, key_type >::value >::type *=nullptr)
Definition: counter.hpp:318
friend bool operator!=(const CounterEngine< Generator > &eng1, const CounterEngine< Generator > &eng2)
Definition: counter.hpp:438
CounterEngine(const key_type &k)
Definition: counter.hpp:308
void seed(result_type s)
Definition: counter.hpp:310
void seed(const key_type &k)
Definition: counter.hpp:326
void discard(result_type nskip)
Definition: counter.hpp:392
friend std::basic_istream< CharT, Traits > & operator>>(std::basic_istream< CharT, Traits > &is, CounterEngine< Generator > &eng)
Definition: counter.hpp:461
void increment_block_safe(std::array< T, K > &, std::array< std::array< T, K >, Blocks > &, std::false_type)
Definition: counter.hpp:129
void increment_single(std::array< T, K > &, std::false_type)
Definition: counter.hpp:44