Eigen  5.0.1-dev+60122df6
 
Loading...
Searching...
No Matches
RandomImpl.h
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
16namespace Eigen {
17
18namespace internal {
19
20/****************************************************************************
21 * Implementation of random *
22 ****************************************************************************/
23
24template <typename Scalar, bool IsComplex, bool IsInteger>
25struct random_default_impl {};
26
27template <typename Scalar>
28struct random_impl : random_default_impl<Scalar, NumTraits<Scalar>::IsComplex, NumTraits<Scalar>::IsInteger> {};
29
30template <typename Scalar>
31struct random_retval {
32 typedef Scalar type;
33};
34
35template <typename Scalar>
36inline EIGEN_MATHFUNC_RETVAL(random, Scalar) random(const Scalar& x, const Scalar& y) {
37 return EIGEN_MATHFUNC_IMPL(random, Scalar)::run(x, y);
38}
39
40template <typename Scalar>
41inline 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
46struct eigen_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
54template <typename Scalar>
55struct random_bits_impl {
56 EIGEN_STATIC_ASSERT(std::is_unsigned<Scalar>::value, SCALAR MUST BE A BUILT - IN UNSIGNED INTEGER)
57 using RandomDevice = eigen_random_device;
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) {
67 RandomReturnType r = RandomDevice::run();
68 randomBits |= static_cast<Scalar>(r) << shift;
69 }
70 // clear the excess bits
71 randomBits &= mask;
72 return randomBits;
73 }
74};
75
76template <typename BitsType>
77EIGEN_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
82template <typename Scalar, bool BuiltIn = std::is_floating_point<Scalar>::value>
83struct random_float_impl {
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
104template <typename Scalar>
105struct 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
121template <bool Specialize = (sizeof(long double) == 2 * sizeof(uint64_t)) &&
122 ((std::numeric_limits<long double>::digits != (2 * std::numeric_limits<double>::digits)))>
123struct random_longdouble_impl {
124 static constexpr int Size = sizeof(long double);
125 static constexpr EIGEN_DEVICE_FUNC 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#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
135 randomBits[0] |= getRandomBits<uint64_t>(numLowBits);
136 randomBits[1] |= getRandomBits<uint64_t>(numHighBits);
137#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
138 randomBits[0] |= getRandomBits<uint64_t>(numHighBits);
139 randomBits[1] |= getRandomBits<uint64_t>(numLowBits);
140#else
141#error Unexpected or undefined __BYTE_ORDER__
142#endif
143 memcpy(&result, &randomBits, Size);
144 result -= 3.0L;
145 return result;
146 }
147};
148template <>
149struct random_longdouble_impl<false> {
150 static constexpr EIGEN_DEVICE_FUNC int mantissaBits() { return NumTraits<double>::digits() - 1; }
151 static EIGEN_DEVICE_FUNC inline long double run(int numRandomBits) {
152 return static_cast<long double>(random_float_impl<double>::run(numRandomBits));
153 }
154};
155template <>
156struct random_float_impl<long double> : random_longdouble_impl<> {};
157#endif
158
159template <typename Scalar>
160struct random_default_impl<Scalar, false, false> {
161 using Impl = random_float_impl<Scalar>;
162 static EIGEN_DEVICE_FUNC inline Scalar run(const Scalar& x, const Scalar& y, int numRandomBits) {
163 Scalar half_x = Scalar(0.5) * x;
164 Scalar half_y = Scalar(0.5) * y;
165 Scalar result = (half_x + half_y) + (half_y - half_x) * run(numRandomBits);
166 // result is in the half-open interval [x, y) -- provided that x < y
167 return result;
168 }
169 static EIGEN_DEVICE_FUNC inline Scalar run(const Scalar& x, const Scalar& y) {
170 return run(x, y, Impl::mantissaBits());
171 }
172 static EIGEN_DEVICE_FUNC inline Scalar run(int numRandomBits) { return Impl::run(numRandomBits); }
173 static EIGEN_DEVICE_FUNC inline Scalar run() { return run(Impl::mantissaBits()); }
174};
175
176template <typename Scalar, bool IsSigned = NumTraits<Scalar>::IsSigned, bool BuiltIn = std::is_integral<Scalar>::value>
177struct random_int_impl;
178
179// random implementation for a built-in unsigned integer type
180template <typename Scalar>
181struct random_int_impl<Scalar, false, true> {
182 static constexpr int kTotalBits = sizeof(Scalar) * CHAR_BIT;
183 static EIGEN_DEVICE_FUNC inline Scalar run(const Scalar& x, const Scalar& y) {
184 if (y <= x) return x;
185 Scalar range = y - x;
186 // handle edge case where [x,y] spans the entire range of Scalar
187 if (range == NumTraits<Scalar>::highest()) return run();
188 Scalar count = range + 1;
189 // calculate the number of random bits needed to fill range
190 int numRandomBits = log2_ceil(count);
191 Scalar randomBits;
192 do {
193 randomBits = getRandomBits<Scalar>(numRandomBits);
194 // if the random draw is outside [0, range), try again (rejection sampling)
195 // in the worst-case scenario, the probability of rejection is: 1/2 - 1/2^numRandomBits < 50%
196 } while (randomBits >= count);
197 Scalar result = x + randomBits;
198 return result;
199 }
200 static EIGEN_DEVICE_FUNC inline Scalar run() { return getRandomBits<Scalar>(kTotalBits); }
201};
202
203// random implementation for a built-in signed integer type
204template <typename Scalar>
205struct random_int_impl<Scalar, true, true> {
206 static constexpr int kTotalBits = sizeof(Scalar) * CHAR_BIT;
207 using BitsType = typename make_unsigned<Scalar>::type;
208 static EIGEN_DEVICE_FUNC inline Scalar run(const Scalar& x, const Scalar& y) {
209 if (y <= x) return x;
210 // Avoid overflow by representing `range` as an unsigned type
211 BitsType range = static_cast<BitsType>(y) - static_cast<BitsType>(x);
212 BitsType randomBits = random_int_impl<BitsType>::run(0, range);
213 // Avoid overflow in the case where `x` is negative and there is a large range so
214 // `randomBits` would also be negative if cast to `Scalar` first.
215 Scalar result = static_cast<Scalar>(static_cast<BitsType>(x) + randomBits);
216 return result;
217 }
218 static EIGEN_DEVICE_FUNC inline Scalar run() { return static_cast<Scalar>(getRandomBits<BitsType>(kTotalBits)); }
219};
220
221// todo: custom integers
222template <typename Scalar, bool IsSigned>
223struct random_int_impl<Scalar, IsSigned, false> {
224 static EIGEN_DEVICE_FUNC inline Scalar run(const Scalar&, const Scalar&) { return run(); }
225 static EIGEN_DEVICE_FUNC inline Scalar run() {
226 eigen_assert(std::false_type::value && "RANDOM FOR CUSTOM INTEGERS NOT YET SUPPORTED");
227 return Scalar(0);
228 }
229};
230
231template <typename Scalar>
232struct random_default_impl<Scalar, false, true> : random_int_impl<Scalar> {};
233
234template <>
235struct random_impl<bool> {
236 static EIGEN_DEVICE_FUNC inline bool run(const bool& x, const bool& y) {
237 if (y <= x) return x;
238 return run();
239 }
240 static EIGEN_DEVICE_FUNC inline bool run() { return getRandomBits<unsigned>(1) ? true : false; }
241};
242
243template <typename Scalar>
244struct random_default_impl<Scalar, true, false> {
245 typedef typename NumTraits<Scalar>::Real RealScalar;
246 using Impl = random_impl<RealScalar>;
247 static EIGEN_DEVICE_FUNC inline Scalar run(const Scalar& x, const Scalar& y, int numRandomBits) {
248 return Scalar(Impl::run(x.real(), y.real(), numRandomBits), Impl::run(x.imag(), y.imag(), numRandomBits));
249 }
250 static EIGEN_DEVICE_FUNC inline Scalar run(const Scalar& x, const Scalar& y) {
251 return Scalar(Impl::run(x.real(), y.real()), Impl::run(x.imag(), y.imag()));
252 }
253 static EIGEN_DEVICE_FUNC inline Scalar run(int numRandomBits) {
254 return Scalar(Impl::run(numRandomBits), Impl::run(numRandomBits));
255 }
256 static EIGEN_DEVICE_FUNC inline Scalar run() { return Scalar(Impl::run(), Impl::run()); }
257};
258
259} // namespace internal
260} // namespace Eigen
261
262#endif // EIGEN_RANDOM_IMPL_H
Namespace containing all symbols from the Eigen library.
Definition B01_Experimental.dox:1