vSMC
vSMC: Scalable Monte Carlo
philox.hpp
Go to the documentation of this file.
1 //============================================================================
2 // vSMC/include/vsmc/rng/philox.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_PHILOX_HPP
33 #define VSMC_RNG_PHILOX_HPP
34 
36 #include <vsmc/rng/counter.hpp>
37 
38 #ifdef VSMC_MSVC
39 #include <intrin.h>
40 #endif
41 
42 #define VSMC_STATIC_ASSERT_RNG_PHILOX_RESULT_TYPE(ResultType) \
43  VSMC_STATIC_ASSERT(((sizeof(ResultType) == sizeof(std::uint32_t) && \
44  std::is_unsigned<ResultType>::value) || \
45  (sizeof(ResultType) == sizeof(std::uint64_t) && \
46  std::is_unsigned<ResultType>::value)), \
47  "**PhiloxGenerator** USED WITH ResultType OTHER THAN UNSIGNED 32/64 " \
48  "BITS INTEGER")
49 
50 #define VSMC_STATIC_ASSERT_RNG_PHILOX_SIZE(K) \
51  VSMC_STATIC_ASSERT((K == 2 || K == 4), \
52  "**PhiloxGenerator** USED WITH SIZE OTHER THAN 2 OR 4")
53 
54 #define VSMC_STATIC_ASSERT_RNG_PHILOX \
55  VSMC_STATIC_ASSERT_RNG_PHILOX_RESULT_TYPE(ResultType); \
56  VSMC_STATIC_ASSERT_RNG_PHILOX_SIZE(K);
57 
58 #define VSMC_DEFINE_RNG_PHILOX_WELY_CONSTANT(T, I, val) \
59  template <> \
60  class PhiloxWeylConstant<T, I> : public std::integral_constant<T, val> \
61  { \
62  }; // class PhiloxWeylConstant
63 
64 #define VSMC_DEFINE_RNG_PHILOX_ROUND_CONSTANT(T, K, I, val) \
65  template <> \
66  class PhiloxRoundConstant<T, K, I> \
67  : public std::integral_constant<T, val> \
68  { \
69  }; // PhiloxRoundConstant
70 
73 #ifndef VSMC_RNG_PHILOX_ROUNDS
74 #define VSMC_RNG_PHILOX_ROUNDS 10
75 #endif
76 
79 #ifndef VSMC_RNG_PHILOX_VECTOR_LENGTH
80 #define VSMC_RNG_PHILOX_VECTOR_LENGTH 4
81 #endif
82 
83 namespace vsmc
84 {
85 
86 namespace internal
87 {
88 
89 template <typename, std::size_t>
91 
94 
96  std::uint64_t, 0, UINT64_C(0x9E3779B97F4A7C15))
98  std::uint64_t, 1, UINT64_C(0xBB67AE8584CAA73B))
99 
100 template <typename, std::size_t, std::size_t>
102 
104  std::uint32_t, 2, 0, UINT32_C(0xD256D193))
105 
107  std::uint32_t, 4, 0, UINT32_C(0xD2511F53))
109  std::uint32_t, 4, 1, UINT32_C(0xCD9E8D57))
110 
112  std::uint64_t, 2, 0, UINT64_C(0xD2B74407B1CE6E93))
113 
115  std::uint64_t, 4, 0, UINT64_C(0xD2E7470EE14C6C93))
117  std::uint64_t, 4, 1, UINT64_C(0xCA5A826395121157))
118 
119 template <std::size_t K, std::size_t I>
120 inline void philox_hilo(std::uint32_t b, std::uint32_t &hi, std::uint32_t &lo)
121 {
122  std::uint64_t prod = static_cast<std::uint64_t>(b) *
123  static_cast<std::uint64_t>(
125  hi = static_cast<std::uint32_t>(prod >> 32);
126  lo = static_cast<std::uint32_t>(prod);
127 }
128 
129 #if VSMC_HAS_INT128
130 
131 template <std::size_t K, std::size_t I>
132 inline void philox_hilo(std::uint64_t b, std::uint64_t &hi, std::uint64_t &lo)
133 {
134  unsigned VSMC_INT128 prod =
135  static_cast<unsigned VSMC_INT128>(b) *
136  static_cast<unsigned VSMC_INT128>(
138  hi = static_cast<std::uint64_t>(prod >> 64);
139  lo = static_cast<std::uint64_t>(prod);
140 }
141 
142 #elif defined(VSMC_MSVC) // VSMC_HAS_INT128
143 
144 template <std::size_t K, std::size_t I>
145 inline void philox_hilo(std::uint64_t b, std::uint64_t &hi, std::uint64_t &lo)
146 {
148 }
149 
150 #else // VSMC_HAS_INT128
151 
152 template <std::size_t K, std::size_t I>
154 {
156  const unsigned whalf = 32;
157  const std::uint64_t lomask = (static_cast<std::uint64_t>(1) << whalf) - 1;
158 
159  lo = static_cast<std::uint64_t>(a * b);
160 
161  const std::uint64_t ahi = a >> whalf;
162  const std::uint64_t alo = a & lomask;
163  const std::uint64_t bhi = b >> whalf;
164  const std::uint64_t blo = b & lomask;
165 
166  const std::uint64_t ahbl = ahi * blo;
167  const std::uint64_t albh = alo * bhi;
168 
169  const std::uint64_t ahbl_albh = ((ahbl & lomask) + (albh & lomask));
170 
171  hi = ahi * bhi + (ahbl >> whalf) + (albh >> whalf);
172  hi += ahbl_albh >> whalf;
173  hi += ((lo >> whalf) < (ahbl_albh & lomask));
174 }
175 
176 #endif // VSMC_HAS_INT128
177 
178 template <typename T, std::size_t K, std::size_t N, bool = (N > 1)>
180 {
181  public:
182  static void eval(std::array<T, K / 2> &) {}
183 }; // class PhiloxBumpKey
184 
185 template <typename T, std::size_t N>
186 class PhiloxBumpKey<T, 2, N, true>
187 {
188  public:
189  static void eval(std::array<T, 1> &par)
190  {
191  std::get<0>(par) += PhiloxWeylConstant<T, 0>::value;
192  }
193 }; // class PhiloxBumpKey
194 
195 template <typename T, std::size_t N>
196 class PhiloxBumpKey<T, 4, N, true>
197 {
198  public:
199  static void eval(std::array<T, 2> &par)
200  {
201  std::get<0>(par) += PhiloxWeylConstant<T, 0>::value;
202  std::get<1>(par) += PhiloxWeylConstant<T, 1>::value;
203  }
204 }; // class PhiloxBumpKey
205 
206 template <typename T, std::size_t K, std::size_t N, bool = (N > 0)>
208 {
209  public:
210  static void eval(std::array<T, K> &, const std::array<T, K / 2> &) {}
211 }; // class PhiloxRound
212 
213 template <typename T, std::size_t N>
214 class PhiloxRound<T, 2, N, true>
215 {
216  public:
217  static void eval(std::array<T, 2> &state, const std::array<T, 1> &par)
218  {
219  T hi = 0;
220  T lo = 0;
221  philox_hilo<2, 0>(std::get<0>(state), hi, lo);
222  hi ^= std::get<0>(par);
223  std::get<0>(state) = hi ^ std::get<1>(state);
224  std::get<1>(state) = lo;
225  }
226 }; // class PhiloxRound
227 
228 template <typename T, std::size_t N>
229 class PhiloxRound<T, 4, N, true>
230 {
231  public:
232  static void eval(std::array<T, 4> &state, const std::array<T, 2> &par)
233  {
234  T hi0 = 0;
235  T lo1 = 0;
236  T hi2 = 0;
237  T lo3 = 0;
238  philox_hilo<4, 1>(std::get<2>(state), hi0, lo1);
239  philox_hilo<4, 0>(std::get<0>(state), hi2, lo3);
240 
241  hi0 ^= std::get<0>(par);
242  hi2 ^= std::get<1>(par);
243  std::get<0>(state) = hi0 ^ std::get<1>(state);
244  std::get<1>(state) = lo1;
245  std::get<2>(state) = hi2 ^ std::get<3>(state);
246  std::get<3>(state) = lo3;
247  }
248 }; // class PhiloxRound
249 
250 } // namespace vsmc::internal
251 
254 template <typename ResultType, std::size_t K = VSMC_RNG_PHILOX_VECTOR_LENGTH,
255  std::size_t Rounds = VSMC_RNG_PHILOX_ROUNDS>
257 {
258  public:
259  using result_type = ResultType;
260  using ctr_type = std::array<ResultType, K>;
261  using key_type = std::array<ResultType, K / 2>;
262 
264 
265  static constexpr std::size_t size() { return K; }
266 
267  void reset(const key_type &) {}
268 
269  void operator()(ctr_type &ctr, const key_type &key,
270  std::array<result_type, K> &buffer) const
271  {
272  union {
273  std::array<ctr_type, 1> state;
274  std::array<result_type, size()> result;
275  } buf;
276 
277  increment(ctr);
278  buf.state.front() = ctr;
279  key_type par = key;
280  generate<0>(buf.state, par, std::true_type());
281  buffer = buf.result;
282  }
283 
284  std::size_t operator()(ctr_type &ctr, const key_type &key, std::size_t n,
285  result_type *r) const
286  {
287  const std::size_t Blocks = 8;
288  const std::size_t M = size() * Blocks;
289  const std::size_t m = n / M;
290  increment(ctr, m, reinterpret_cast<ctr_type *>(r));
291  std::array<ctr_type, Blocks> *s =
292  reinterpret_cast<std::array<ctr_type, Blocks> *>(r);
293  for (std::size_t i = 0; i != m; ++i) {
294  key_type par = key;
295  generate<0>(s[i], par, std::true_type());
296  }
297 
298  return m * M;
299  }
300 
301  private:
302  template <std::size_t, std::size_t Blocks>
303  void generate(
304  std::array<ctr_type, Blocks> &, key_type &, std::false_type) const
305  {
306  }
307 
308  template <std::size_t N, std::size_t Blocks>
309  void generate(std::array<ctr_type, Blocks> &state, key_type &par,
310  std::true_type) const
311  {
313  round<N, 0>(state, par, std::true_type());
314  generate<N + 1>(
315  state, par, std::integral_constant < bool, N<Rounds>());
316  }
317 
318  template <std::size_t, std::size_t, std::size_t Blocks>
319  void round(
320  std::array<ctr_type, Blocks> &, key_type &, std::false_type) const
321  {
322  }
323 
324  template <std::size_t N, std::size_t B, std::size_t Blocks>
325  void round(std::array<ctr_type, Blocks> &state, key_type &par,
326  std::true_type) const
327  {
328  internal::PhiloxRound<ResultType, K, N>::eval(std::get<B>(state), par);
329  round<N, B + 1>(
330  state, par, std::integral_constant<bool, B + 1 < Blocks>());
331  }
332 }; // class PhiloxGenerator
333 
336 template <typename ResultType, std::size_t K = VSMC_RNG_PHILOX_VECTOR_LENGTH,
337  std::size_t Rounds = VSMC_RNG_PHILOX_ROUNDS>
339 
343 
347 
351 
355 
359 
363 
364 } // namespace vsmc
365 
366 #endif // VSMC_RNG_PHILOX_HPP
#define UINT64_C(x)
Definition: opencl.h:42
Definition: monitor.hpp:49
#define VSMC_DEFINE_RNG_PHILOX_ROUND_CONSTANT(T, K, I, val)
Definition: philox.hpp:64
static void eval(std::array< T, 4 > &state, const std::array< T, 2 > &par)
Definition: philox.hpp:232
static void eval(std::array< T, 2 > &state, const std::array< T, 1 > &par)
Definition: philox.hpp:217
static constexpr std::size_t size()
Definition: philox.hpp:265
uint uint32_t
Definition: opencl.h:39
void increment(std::array< T, K > &ctr)
Increment a counter by one.
Definition: counter.hpp:62
static void eval(std::array< T, K > &, const std::array< T, K/2 > &)
Definition: philox.hpp:210
void operator()(ctr_type &ctr, const key_type &key, std::array< result_type, K > &buffer) const
Definition: philox.hpp:269
ulong uint64_t
Definition: opencl.h:40
STL namespace.
Counter based RNG engine.
Definition: counter.hpp:290
void reset(const key_type &)
Definition: philox.hpp:267
ResultType result_type
Definition: philox.hpp:259
void philox_hilo(std::uint32_t b, std::uint32_t &hi, std::uint32_t &lo)
Definition: philox.hpp:120
#define VSMC_RNG_PHILOX_VECTOR_LENGTH
PhiloxGenerator default vector length.
Definition: philox.hpp:80
static void eval(std::array< T, 2 > &par)
Definition: philox.hpp:199
static void eval(std::array< T, 1 > &par)
Definition: philox.hpp:189
Philox RNG generator.
Definition: philox.hpp:256
#define VSMC_RNG_PHILOX_ROUNDS
PhiloxGenerator default rounds.
Definition: philox.hpp:74
#define VSMC_DEFINE_RNG_PHILOX_WELY_CONSTANT(T, I, val)
Definition: philox.hpp:58
std::array< ResultType, K > ctr_type
Definition: philox.hpp:260
std::size_t operator()(ctr_type &ctr, const key_type &key, std::size_t n, result_type *r) const
Definition: philox.hpp:284
#define UINT32_C(x)
Definition: opencl.h:41
std::array< ResultType, K/2 > key_type
Definition: philox.hpp:261
static void eval(std::array< T, K/2 > &)
Definition: philox.hpp:182
#define VSMC_STATIC_ASSERT_RNG_PHILOX
Definition: philox.hpp:54