Eigen-Contrib  5.0.1
 
Loading...
Searching...
No Matches
TensorRandom.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2016 Benoit Steiner <benoit.steiner.goog@gmail.com>
5// Copyright (C) 2018 Mehdi Goli <eigen@codeplay.com> Codeplay Software Ltd.
6//
7// This Source Code Form is subject to the terms of the Mozilla
8// Public License v. 2.0. If a copy of the MPL was not distributed
9// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
10// SPDX-License-Identifier: MPL-2.0
11
12#ifndef EIGEN_TENSOR_TENSOR_RANDOM_H
13#define EIGEN_TENSOR_TENSOR_RANDOM_H
14
15// IWYU pragma: private
16#include "./InternalHeaderCheck.h"
17
18namespace Eigen {
19namespace internal {
20
21EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE uint64_t get_random_seed() {
22#if defined(EIGEN_GPU_COMPILE_PHASE)
23 // We don't support 3d kernels since we currently only use 1 and
24 // 2d kernels.
25 eigen_assert(threadIdx.z == 0);
26 return blockIdx.x * blockDim.x + threadIdx.x + gridDim.x * blockDim.x * (blockIdx.y * blockDim.y + threadIdx.y);
27#else
28 // Rely on Eigen's random implementation.
29 return random<uint64_t>();
30#endif
31}
32
33EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE unsigned PCG_XSH_RS_generator(uint64_t* state, uint64_t stream) {
34 // TODO: Unify with the implementation in the non blocking thread pool.
35 uint64_t current = *state;
36 // Update the internal state
37 *state = current * 6364136223846793005ULL + (stream << 1 | 1);
38 // Generate the random output (using the PCG-XSH-RS scheme)
39 return static_cast<unsigned>((current ^ (current >> 22)) >> (22 + (current >> 61)));
40}
41
42EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE uint64_t PCG_XSH_RS_state(uint64_t seed) {
43 seed = seed ? seed : get_random_seed();
44 return seed * 6364136223846793005ULL + 0xda3e39cb94b95bdbULL;
45}
46
47// The state that the PCG stream of element `index` starts from: the base state stepped along the SplitMix64 Weyl
48// sequence and mixed (splitmix64_mix, RandomImpl.h), so it is a pure function of (seed, index) and unrelated between
49// neighbouring indices. The mixing matters because PCG_XSH_RS_generator derives its first output from the state it
50// is handed: without it, the low bits of base + index * increment would pass straight into that output.
51EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE uint64_t PCG_XSH_RS_state_for_index(uint64_t base_state, uint64_t index) {
52 return splitmix64_mix(base_state + index * 0x9e3779b97f4a7c15ULL);
53}
54
55template <typename T>
56EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T RandomToTypeUniform(uint64_t* state, uint64_t stream) {
57 unsigned rnd = PCG_XSH_RS_generator(state, stream);
58 return static_cast<T>(rnd);
59}
60
61template <>
62EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool RandomToTypeUniform<bool>(uint64_t* state, uint64_t stream) {
63 unsigned rnd = PCG_XSH_RS_generator(state, stream);
64 return (rnd & 0x1) != 0;
65}
66
67template <>
68EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Eigen::half RandomToTypeUniform<Eigen::half>(uint64_t* state, uint64_t stream) {
69 // Generate 10 random bits for the mantissa, merge with exponent.
70 unsigned rnd = PCG_XSH_RS_generator(state, stream);
71 const uint16_t half_bits = static_cast<uint16_t>(rnd & 0x3ffu) | (static_cast<uint16_t>(15) << 10);
72 Eigen::half result = Eigen::numext::bit_cast<Eigen::half>(half_bits);
73 return result - Eigen::half(1.0f);
74}
75
76template <>
77EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Eigen::bfloat16 RandomToTypeUniform<Eigen::bfloat16>(uint64_t* state,
78 uint64_t stream) {
79 // Generate 7 random bits for the mantissa, merge with exponent.
80 unsigned rnd = PCG_XSH_RS_generator(state, stream);
81 const uint16_t half_bits = static_cast<uint16_t>(rnd & 0x7fu) | (static_cast<uint16_t>(127) << 7);
82 Eigen::bfloat16 result = Eigen::numext::bit_cast<Eigen::bfloat16>(half_bits);
83 return result - Eigen::bfloat16(1.0f);
84}
85
86template <>
87EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE float RandomToTypeUniform<float>(uint64_t* state, uint64_t stream) {
88 typedef union {
89 uint32_t raw;
90 float fp;
91 } internal;
92 internal result;
93 // Generate 23 random bits for the mantissa.
94 const unsigned rnd = PCG_XSH_RS_generator(state, stream);
95 result.raw = rnd & 0x7fffffu;
96 // Set the exponent.
97 result.raw |= (static_cast<uint32_t>(127) << 23);
98 return result.fp - 1.0f;
99}
100
101template <>
102EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE double RandomToTypeUniform<double>(uint64_t* state, uint64_t stream) {
103 typedef union {
104 uint64_t raw;
105 double dp;
106 } internal;
107 internal result;
108 result.raw = 0;
109 // Generate 52 random bits for the mantissa
110 // First generate the upper 20 bits
111 unsigned rnd1 = PCG_XSH_RS_generator(state, stream) & 0xfffffu;
112 // Then generate the lower 32 bits.
113 unsigned rnd2 = PCG_XSH_RS_generator(state, stream);
114 result.raw = (static_cast<uint64_t>(rnd1) << 32) | rnd2;
115 // Set the exponent
116 result.raw |= (static_cast<uint64_t>(1023) << 52);
117 // Return the final result
118 return result.dp - 1.0;
119}
120
121template <>
122EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::complex<float> RandomToTypeUniform<std::complex<float> >(uint64_t* state,
123 uint64_t stream) {
124 const float real = RandomToTypeUniform<float>(state, stream);
125 const float imag = RandomToTypeUniform<float>(state, stream);
126 return std::complex<float>(real, imag);
127}
128template <>
129EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::complex<double> RandomToTypeUniform<std::complex<double> >(uint64_t* state,
130 uint64_t stream) {
131 const double real = RandomToTypeUniform<double>(state, stream);
132 const double imag = RandomToTypeUniform<double>(state, stream);
133 return std::complex<double>(real, imag);
134}
135
136// Element i is drawn from its own PCG stream, seeded from (seed, i) by PCG_XSH_RS_state_for_index. The functor
137// therefore holds no evolving state: a fill is the same on every device, thread count and packet width, sharing
138// the functor between threads is race-free, and the copy every GPU thread receives cannot replay the same numbers.
139template <typename T>
140class UniformRandomGenerator {
141 public:
142 static constexpr bool PacketAccess = true;
143
144 // Uses the given "seed" if non-zero, otherwise uses a random seed.
145 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE UniformRandomGenerator(uint64_t seed = 0) : m_state(PCG_XSH_RS_state(seed)) {}
146
147 template <typename Index>
148 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T operator()(Index i) const {
149 const uint64_t index = static_cast<uint64_t>(i);
150 uint64_t state = PCG_XSH_RS_state_for_index(m_state, index);
151 return RandomToTypeUniform<T>(&state, index);
152 }
153
154 template <typename Packet, typename Index>
155 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(Index i) const {
156 const int packetSize = internal::unpacket_traits<Packet>::size;
157 EIGEN_ALIGN_TO_BOUNDARY(unpacket_traits<Packet>::alignment) T values[packetSize];
158 EIGEN_UNROLL_LOOP
159 for (int j = 0; j < packetSize; ++j) {
160 values[j] = (*this)(i + j);
161 }
162 return internal::pload<Packet>(values);
163 }
164
165 private:
166 uint64_t m_state;
167};
168
169template <typename Scalar>
170struct functor_traits<UniformRandomGenerator<Scalar> > {
171 enum {
172 // Rough estimate for floating point, multiplied by ceil(sizeof(T) / sizeof(float)).
173 Cost = 12 * NumTraits<Scalar>::AddCost * ((sizeof(Scalar) + sizeof(float) - 1) / sizeof(float)),
174 PacketAccess = UniformRandomGenerator<Scalar>::PacketAccess,
175 // Element i is a pure function of (seed, i), so re-evaluating the expression or visiting the indices in
176 // block order reproduces the same fill. This is what lets the nullary evaluator serve blocks.
177 IsRepeatable = true
178 };
179};
180
181template <typename T>
182EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T RandomToTypeNormal(uint64_t* state, uint64_t stream) {
183 // Use the ratio of uniform method to generate numbers following a normal
184 // distribution. See for example Numerical Recipes chapter 7.3.9 for the
185 // details.
186 T u, v, q;
187 do {
188 u = RandomToTypeUniform<T>(state, stream);
189 v = T(1.7156) * (RandomToTypeUniform<T>(state, stream) - T(0.5));
190 const T x = u - T(0.449871);
191 const T y = numext::abs(v) + T(0.386595);
192 q = x * x + y * (T(0.196) * y - T(0.25472) * x);
193 // u == 0 is outside the acceptance region (q > 0.27597 there), but -4 * log(0) * 0 * 0 is NaN, so the last test
194 // would accept it and return v / 0 = +/-inf.
195 } while (u == T(0) || (q > T(0.27597) && (q > T(0.27846) || v * v > T(-4) * numext::log(u) * u * u)));
196
197 return v / u;
198}
199
200// For 16-bit types, compute the deviate in float and round once. Running the
201// rejection algorithm above directly in 16-bit arithmetic truncates the tails
202// (|v/u| is limited by the coarse uniform grid) and emits NaN/Inf: the 16-bit
203// uniform draw is exactly 0 with probability 2^-10 (half) / 2^-7 (bfloat16),
204// so log(u) = -inf poisons the acceptance test and v/u returns +/-inf.
205template <>
206EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Eigen::half RandomToTypeNormal<Eigen::half>(uint64_t* state, uint64_t stream) {
207 return Eigen::half(RandomToTypeNormal<float>(state, stream));
208}
209template <>
210EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Eigen::bfloat16 RandomToTypeNormal<Eigen::bfloat16>(uint64_t* state,
211 uint64_t stream) {
212 return Eigen::bfloat16(RandomToTypeNormal<float>(state, stream));
213}
214
215template <>
216EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::complex<float> RandomToTypeNormal<std::complex<float> >(uint64_t* state,
217 uint64_t stream) {
218 const float real = RandomToTypeNormal<float>(state, stream);
219 const float imag = RandomToTypeNormal<float>(state, stream);
220 return std::complex<float>(real, imag);
221}
222template <>
223EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::complex<double> RandomToTypeNormal<std::complex<double> >(uint64_t* state,
224 uint64_t stream) {
225 const double real = RandomToTypeNormal<double>(state, stream);
226 const double imag = RandomToTypeNormal<double>(state, stream);
227 return std::complex<double>(real, imag);
228}
229
230// Per-element streams as in UniformRandomGenerator; the rejection loop of RandomToTypeNormal draws as many values
231// as it needs from element i's own stream.
232template <typename T>
233class NormalRandomGenerator {
234 public:
235 static constexpr bool PacketAccess = true;
236
237 // Uses the given "seed" if non-zero, otherwise uses a random seed.
238 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NormalRandomGenerator(uint64_t seed = 0) : m_state(PCG_XSH_RS_state(seed)) {}
239
240 template <typename Index>
241 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T operator()(Index i) const {
242 const uint64_t index = static_cast<uint64_t>(i);
243 uint64_t state = PCG_XSH_RS_state_for_index(m_state, index);
244 return RandomToTypeNormal<T>(&state, index);
245 }
246
247 template <typename Packet, typename Index>
248 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(Index i) const {
249 const int packetSize = internal::unpacket_traits<Packet>::size;
250 EIGEN_ALIGN_TO_BOUNDARY(unpacket_traits<Packet>::alignment) T values[packetSize];
251 EIGEN_UNROLL_LOOP
252 for (int j = 0; j < packetSize; ++j) {
253 values[j] = (*this)(i + j);
254 }
255 return internal::pload<Packet>(values);
256 }
257
258 private:
259 uint64_t m_state;
260};
261
262template <typename Scalar>
263struct functor_traits<NormalRandomGenerator<Scalar> > {
264 enum {
265 // On average, we need to generate about 3 random numbers
266 // 15 mul, 8 add, 1.5 logs
267 Cost = 3 * functor_traits<UniformRandomGenerator<Scalar> >::Cost + 15 * NumTraits<Scalar>::AddCost +
268 8 * NumTraits<Scalar>::AddCost + 3 * functor_traits<scalar_log_op<Scalar> >::Cost / 2,
269 PacketAccess = NormalRandomGenerator<Scalar>::PacketAccess,
270 // Pure in (seed, index) as UniformRandomGenerator is: the rejection loop draws from element i's own stream.
271 IsRepeatable = true
272 };
273};
274
275} // end namespace internal
276} // end namespace Eigen
277
278#endif // EIGEN_TENSOR_TENSOR_RANDOM_H
Namespace containing all symbols from the Eigen library.