RandomImpl.h
Go to the documentation of this file.
1 // This file is part of Eigen, a lightweight C++ template library
2 // for linear algebra.
3 //
4 // Copyright (C) 2024 Charles Schlosser <cs.schlosser@gmail.com>
5 //
6 // This Source Code Form is subject to the terms of the Mozilla
7 // Public License v. 2.0. If a copy of the MPL was not distributed
8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 
10 #ifndef EIGEN_RANDOM_IMPL_H
11 #define EIGEN_RANDOM_IMPL_H
12 
13 // IWYU pragma: private
14 #include "./InternalHeaderCheck.h"
15 
16 namespace Eigen {
17 
18 namespace internal {
19 
20 /****************************************************************************
21  * Implementation of random *
22  ****************************************************************************/
23 
24 template <typename Scalar, bool IsComplex, bool IsInteger>
26 
27 template <typename Scalar>
28 struct random_impl : random_default_impl<Scalar, NumTraits<Scalar>::IsComplex, NumTraits<Scalar>::IsInteger> {};
29 
30 template <typename Scalar>
31 struct random_retval {
32  typedef Scalar type;
33 };
34 
35 template <typename Scalar>
36 inline EIGEN_MATHFUNC_RETVAL(random, Scalar) random(const Scalar& x, const Scalar& y) {
37  return EIGEN_MATHFUNC_IMPL(random, Scalar)::run(x, y);
38 }
39 
40 template <typename Scalar>
41 inline EIGEN_MATHFUNC_RETVAL(random, Scalar) random() {
42  return EIGEN_MATHFUNC_IMPL(random, Scalar)::run();
43 }
44 
45 // TODO: replace or provide alternatives to this, e.g. std::random_device
47  using ReturnType = int;
48  static constexpr int Entropy = meta_floor_log2<(unsigned int)(RAND_MAX) + 1>::value;
49  static constexpr ReturnType Highest = RAND_MAX;
50  static EIGEN_DEVICE_FUNC inline ReturnType run() { return std::rand(); }
51 };
52 
53 // Fill a built-in unsigned integer with numRandomBits beginning with the least significant bit
54 template <typename Scalar>
56  EIGEN_STATIC_ASSERT(std::is_unsigned<Scalar>::value, SCALAR MUST BE A BUILT - IN UNSIGNED INTEGER)
58  using RandomReturnType = typename RandomDevice::ReturnType;
59  static constexpr int kEntropy = RandomDevice::Entropy;
60  static constexpr int kTotalBits = sizeof(Scalar) * CHAR_BIT;
61  // return a Scalar filled with numRandomBits beginning from the least significant bit
62  static EIGEN_DEVICE_FUNC inline Scalar run(int numRandomBits) {
63  eigen_assert((numRandomBits >= 0) && (numRandomBits <= kTotalBits));
64  const Scalar mask = Scalar(-1) >> ((kTotalBits - numRandomBits) & (kTotalBits - 1));
65  Scalar randomBits = 0;
66  for (int shift = 0; shift < numRandomBits; shift += kEntropy) {
68  randomBits |= static_cast<Scalar>(r) << shift;
69  }
70  // clear the excess bits
71  randomBits &= mask;
72  return randomBits;
73  }
74 };
75 
76 template <typename BitsType>
77 EIGEN_DEVICE_FUNC inline BitsType getRandomBits(int numRandomBits) {
78  return random_bits_impl<BitsType>::run(numRandomBits);
79 }
80 
81 // random implementation for a built-in floating point type
84  using BitsType = typename numext::get_integer_by_size<sizeof(Scalar)>::unsigned_type;
85  static constexpr EIGEN_DEVICE_FUNC inline int mantissaBits() {
86  const int digits = NumTraits<Scalar>::digits();
87  return digits - 1;
88  }
89  static EIGEN_DEVICE_FUNC inline Scalar run(int numRandomBits) {
90  eigen_assert(numRandomBits >= 0 && numRandomBits <= mantissaBits());
91  BitsType randomBits = getRandomBits<BitsType>(numRandomBits);
92  // if fewer than MantissaBits is requested, shift them to the left
93  randomBits <<= (mantissaBits() - numRandomBits);
94  // randomBits is in the half-open interval [2,4)
95  randomBits |= numext::bit_cast<BitsType>(Scalar(2));
96  // result is in the half-open interval [-1,1)
97  Scalar result = numext::bit_cast<Scalar>(randomBits) - Scalar(3);
98  return result;
99  }
100 };
101 // random implementation for a custom floating point type
102 // uses double as the implementation with a mantissa with a size equal to either the target scalar's mantissa or that of
103 // double, whichever is smaller
104 template <typename Scalar>
105 struct random_float_impl<Scalar, false> {
106  static EIGEN_DEVICE_FUNC inline int mantissaBits() {
107  const int digits = NumTraits<Scalar>::digits();
108  constexpr int kDoubleDigits = NumTraits<double>::digits();
109  return numext::mini(digits, kDoubleDigits) - 1;
110  }
111  static EIGEN_DEVICE_FUNC inline Scalar run(int numRandomBits) {
112  eigen_assert(numRandomBits >= 0 && numRandomBits <= mantissaBits());
113  Scalar result = static_cast<Scalar>(random_float_impl<double>::run(numRandomBits));
114  return result;
115  }
116 };
117 
118 #if !EIGEN_COMP_NVCC
119 // random implementation for long double
120 // this specialization is not compatible with double-double scalars
121 template <bool Specialize = (sizeof(long double) == 2 * sizeof(uint64_t)) &&
122  ((std::numeric_limits<long double>::digits != (2 * std::numeric_limits<double>::digits)))>
124  static constexpr int Size = sizeof(long double);
125  static constexpr EIGEN_DEVICE_FUNC inline int mantissaBits() { return NumTraits<long double>::digits() - 1; }
126  static EIGEN_DEVICE_FUNC inline long double run(int numRandomBits) {
127  eigen_assert(numRandomBits >= 0 && numRandomBits <= mantissaBits());
128  EIGEN_USING_STD(memcpy);
129  int numLowBits = numext::mini(numRandomBits, 64);
130  int numHighBits = numext::maxi(numRandomBits - 64, 0);
131  uint64_t randomBits[2];
132  long double result = 2.0L;
133  memcpy(&randomBits, &result, Size);
134  randomBits[0] |= getRandomBits<uint64_t>(numLowBits);
135  randomBits[1] |= getRandomBits<uint64_t>(numHighBits);
136  memcpy(&result, &randomBits, Size);
137  result -= 3.0L;
138  return result;
139  }
140 };
141 template <>
142 struct random_longdouble_impl<false> {
143  static constexpr EIGEN_DEVICE_FUNC inline int mantissaBits() { return NumTraits<double>::digits() - 1; }
144  static EIGEN_DEVICE_FUNC inline long double run(int numRandomBits) {
145  return static_cast<long double>(random_float_impl<double>::run(numRandomBits));
146  }
147 };
148 template <>
150 #endif
151 
152 template <typename Scalar>
153 struct random_default_impl<Scalar, false, false> {
155  static EIGEN_DEVICE_FUNC inline Scalar run(const Scalar& x, const Scalar& y, int numRandomBits) {
156  Scalar half_x = Scalar(0.5) * x;
157  Scalar half_y = Scalar(0.5) * y;
158  Scalar result = (half_x + half_y) + (half_y - half_x) * run(numRandomBits);
159  // result is in the half-open interval [x, y) -- provided that x < y
160  return result;
161  }
162  static EIGEN_DEVICE_FUNC inline Scalar run(const Scalar& x, const Scalar& y) {
163  return run(x, y, Impl::mantissaBits());
164  }
165  static EIGEN_DEVICE_FUNC inline Scalar run(int numRandomBits) { return Impl::run(numRandomBits); }
166  static EIGEN_DEVICE_FUNC inline Scalar run() { return run(Impl::mantissaBits()); }
167 };
168 
169 template <typename Scalar, bool IsSigned = NumTraits<Scalar>::IsSigned, bool BuiltIn = std::is_integral<Scalar>::value>
171 
172 // random implementation for a built-in unsigned integer type
173 template <typename Scalar>
174 struct random_int_impl<Scalar, false, true> {
175  static constexpr int kTotalBits = sizeof(Scalar) * CHAR_BIT;
176  static EIGEN_DEVICE_FUNC inline Scalar run(const Scalar& x, const Scalar& y) {
177  if (y <= x) return x;
178  Scalar range = y - x;
179  // handle edge case where [x,y] spans the entire range of Scalar
180  if (range == NumTraits<Scalar>::highest()) return run();
181  Scalar count = range + 1;
182  // calculate the number of random bits needed to fill range
183  int numRandomBits = log2_ceil(count);
184  Scalar randomBits;
185  do {
186  randomBits = getRandomBits<Scalar>(numRandomBits);
187  // if the random draw is outside [0, range), try again (rejection sampling)
188  // in the worst-case scenario, the probability of rejection is: 1/2 - 1/2^numRandomBits < 50%
189  } while (randomBits >= count);
190  Scalar result = x + randomBits;
191  return result;
192  }
193  static EIGEN_DEVICE_FUNC inline Scalar run() { return getRandomBits<Scalar>(kTotalBits); }
194 };
195 
196 // random implementation for a built-in signed integer type
197 template <typename Scalar>
198 struct random_int_impl<Scalar, true, true> {
199  static constexpr int kTotalBits = sizeof(Scalar) * CHAR_BIT;
201  static EIGEN_DEVICE_FUNC inline Scalar run(const Scalar& x, const Scalar& y) {
202  if (y <= x) return x;
203  // Avoid overflow by representing `range` as an unsigned type
204  BitsType range = static_cast<BitsType>(y) - static_cast<BitsType>(x);
205  BitsType randomBits = random_int_impl<BitsType>::run(0, range);
206  // Avoid overflow in the case where `x` is negative and there is a large range so
207  // `randomBits` would also be negative if cast to `Scalar` first.
208  Scalar result = static_cast<Scalar>(static_cast<BitsType>(x) + randomBits);
209  return result;
210  }
211  static EIGEN_DEVICE_FUNC inline Scalar run() { return static_cast<Scalar>(getRandomBits<BitsType>(kTotalBits)); }
212 };
213 
214 // todo: custom integers
215 template <typename Scalar, bool IsSigned>
216 struct random_int_impl<Scalar, IsSigned, false> {
217  static EIGEN_DEVICE_FUNC inline Scalar run(const Scalar&, const Scalar&) { return run(); }
218  static EIGEN_DEVICE_FUNC inline Scalar run() {
219  eigen_assert(std::false_type::value && "RANDOM FOR CUSTOM INTEGERS NOT YET SUPPORTED");
220  return Scalar(0);
221  }
222 };
223 
224 template <typename Scalar>
225 struct random_default_impl<Scalar, false, true> : random_int_impl<Scalar> {};
226 
227 template <>
228 struct random_impl<bool> {
229  static EIGEN_DEVICE_FUNC inline bool run(const bool& x, const bool& y) {
230  if (y <= x) return x;
231  return run();
232  }
233  static EIGEN_DEVICE_FUNC inline bool run() { return getRandomBits<unsigned>(1) ? true : false; }
234 };
235 
236 template <typename Scalar>
237 struct random_default_impl<Scalar, true, false> {
240  static EIGEN_DEVICE_FUNC inline Scalar run(const Scalar& x, const Scalar& y, int numRandomBits) {
241  return Scalar(Impl::run(x.real(), y.real(), numRandomBits), Impl::run(x.imag(), y.imag(), numRandomBits));
242  }
243  static EIGEN_DEVICE_FUNC inline Scalar run(const Scalar& x, const Scalar& y) {
244  return Scalar(Impl::run(x.real(), y.real()), Impl::run(x.imag(), y.imag()));
245  }
246  static EIGEN_DEVICE_FUNC inline Scalar run(int numRandomBits) {
247  return Scalar(Impl::run(numRandomBits), Impl::run(numRandomBits));
248  }
249  static EIGEN_DEVICE_FUNC inline Scalar run() { return Scalar(Impl::run(), Impl::run()); }
250 };
251 
252 } // namespace internal
253 } // namespace Eigen
254 
255 #endif // EIGEN_RANDOM_IMPL_H
#define EIGEN_USING_STD(FUNC)
Definition: Macros.h:1090
#define EIGEN_DEVICE_FUNC
Definition: Macros.h:892
#define eigen_assert(x)
Definition: Macros.h:910
#define EIGEN_MATHFUNC_IMPL(func, scalar)
Definition: MathFunctions.h:64
#define EIGEN_STATIC_ASSERT(X, MSG)
Definition: StaticAssert.h:26
SCALAR Scalar
Definition: bench_gemm.cpp:45
#define SCALAR
Definition: bench_gemm.cpp:22
The matrix class, also used for vectors and row-vectors.
Definition: Eigen/Eigen/src/Core/Matrix.h:186
@ BuiltIn
Definition: Constants.h:311
return int(ret)+1
const Scalar & y
Definition: RandomImpl.h:36
EIGEN_DEVICE_FUNC BitsType getRandomBits(int numRandomBits)
Definition: RandomImpl.h:77
int log2_ceil(const BitsType &x)
Definition: MathFunctions.h:758
EIGEN_MATHFUNC_RETVAL(random, Scalar) random(const Scalar &x
EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE T maxi(const T &x, const T &y)
Definition: MathFunctions.h:926
EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE T mini(const T &x, const T &y)
Definition: MathFunctions.h:920
std::uint64_t uint64_t
Definition: Meta.h:42
Namespace containing all symbols from the Eigen library.
Definition: bench_norm.cpp:70
auto run(Kernel kernel, Args &&... args) -> decltype(kernel(args...))
Definition: gpu_test_helper.h:414
squared absolute value
Definition: GlobalFunctions.h:87
r
Definition: UniformPSDSelfTest.py:20
type
Definition: compute_granudrum_aor.py:141
Definition: Eigen_Colamd.h:49
list x
Definition: plotDoE.py:28
Holds information about the various numeric (i.e. scalar) types allowed by Eigen.
Definition: NumTraits.h:217
Definition: RandomImpl.h:46
static EIGEN_DEVICE_FUNC ReturnType run()
Definition: RandomImpl.h:50
static constexpr int Entropy
Definition: RandomImpl.h:48
static constexpr ReturnType Highest
Definition: RandomImpl.h:49
int ReturnType
Definition: RandomImpl.h:47
Definition: MathFunctions.h:581
Definition: RandomImpl.h:55
typename RandomDevice::ReturnType RandomReturnType
Definition: RandomImpl.h:58
static EIGEN_DEVICE_FUNC Scalar run(int numRandomBits)
Definition: RandomImpl.h:62
static constexpr int kEntropy
Definition: RandomImpl.h:59
static constexpr int kTotalBits
Definition: RandomImpl.h:60
static EIGEN_DEVICE_FUNC Scalar run()
Definition: RandomImpl.h:166
static EIGEN_DEVICE_FUNC Scalar run(const Scalar &x, const Scalar &y, int numRandomBits)
Definition: RandomImpl.h:155
static EIGEN_DEVICE_FUNC Scalar run(int numRandomBits)
Definition: RandomImpl.h:165
static EIGEN_DEVICE_FUNC Scalar run(const Scalar &x, const Scalar &y)
Definition: RandomImpl.h:162
static EIGEN_DEVICE_FUNC Scalar run(const Scalar &x, const Scalar &y, int numRandomBits)
Definition: RandomImpl.h:240
NumTraits< Scalar >::Real RealScalar
Definition: RandomImpl.h:238
static EIGEN_DEVICE_FUNC Scalar run(int numRandomBits)
Definition: RandomImpl.h:246
static EIGEN_DEVICE_FUNC Scalar run()
Definition: RandomImpl.h:249
static EIGEN_DEVICE_FUNC Scalar run(const Scalar &x, const Scalar &y)
Definition: RandomImpl.h:243
Definition: RandomImpl.h:25
static EIGEN_DEVICE_FUNC Scalar run(int numRandomBits)
Definition: RandomImpl.h:111
static EIGEN_DEVICE_FUNC int mantissaBits()
Definition: RandomImpl.h:106
Definition: RandomImpl.h:83
static EIGEN_DEVICE_FUNC Scalar run(int numRandomBits)
Definition: RandomImpl.h:89
typename numext::get_integer_by_size< sizeof(Scalar)>::unsigned_type BitsType
Definition: RandomImpl.h:84
static constexpr EIGEN_DEVICE_FUNC int mantissaBits()
Definition: RandomImpl.h:85
static EIGEN_DEVICE_FUNC bool run()
Definition: RandomImpl.h:233
static EIGEN_DEVICE_FUNC bool run(const bool &x, const bool &y)
Definition: RandomImpl.h:229
Definition: RandomImpl.h:28
static EIGEN_DEVICE_FUNC Scalar run()
Definition: RandomImpl.h:218
static EIGEN_DEVICE_FUNC Scalar run(const Scalar &, const Scalar &)
Definition: RandomImpl.h:217
static EIGEN_DEVICE_FUNC Scalar run(const Scalar &x, const Scalar &y)
Definition: RandomImpl.h:176
static EIGEN_DEVICE_FUNC Scalar run()
Definition: RandomImpl.h:193
typename make_unsigned< Scalar >::type BitsType
Definition: RandomImpl.h:200
static EIGEN_DEVICE_FUNC Scalar run(const Scalar &x, const Scalar &y)
Definition: RandomImpl.h:201
static EIGEN_DEVICE_FUNC Scalar run()
Definition: RandomImpl.h:211
Definition: RandomImpl.h:170
static EIGEN_DEVICE_FUNC long double run(int numRandomBits)
Definition: RandomImpl.h:144
static constexpr EIGEN_DEVICE_FUNC int mantissaBits()
Definition: RandomImpl.h:143
Definition: RandomImpl.h:123
static constexpr EIGEN_DEVICE_FUNC int mantissaBits()
Definition: RandomImpl.h:125
static constexpr int Size
Definition: RandomImpl.h:124
static EIGEN_DEVICE_FUNC long double run(int numRandomBits)
Definition: RandomImpl.h:126
Definition: RandomImpl.h:31
Scalar type
Definition: RandomImpl.h:32
void run(const string &dir_name, LinearSolver *linear_solver_pt, const unsigned nel_1d, bool mess_up_order)
Definition: two_d_poisson_compare_solvers.cc:317