/** * @file Basic.h * @author bab2min (bab2min@gmail.com) * @brief * @version 0.3.3 * @date 2021-03-31 * * @copyright Copyright (c) 2020-2021 * */ #ifndef EIGENRAND_DISTS_BASIC_H #define EIGENRAND_DISTS_BASIC_H namespace Eigen { namespace Rand { namespace constant { static constexpr double pi = 3.1415926535897932; static constexpr double e = 2.7182818284590452; } /** * @brief Base class of all univariate random generators * * @tparam DerivedGen * @tparam Scalar */ template class GenBase { public: /** * @brief generate random values from its distribution * * @tparam Derived * @tparam Urng * @param rows the number of rows being generated * @param cols the number of columns being generated * @param urng c++11-style random number generator * @return * a random matrix expression with a shape `(rows, cols)` */ template inline const CwiseNullaryOp, const Derived> generate(Index rows, Index cols, Urng&& urng) { return { rows, cols, { std::forward(urng), static_cast(*this) } }; } /** * @brief generate random values from its distribution * * @tparam Derived * @tparam Urng * @param o an instance of any type of Eigen::DenseBase * @param urng c++11-style random number generator * @return * a random matrix expression of the same shape as `o` */ template inline const CwiseNullaryOp, const Derived> generateLike(const Derived& o, Urng&& urng) { return { o.rows(), o.cols(), { std::forward(urng), static_cast(*this) } }; } }; /** * @brief Base class of all multivariate random vector generators * * @tparam DerivedGen * @tparam _Scalar * @tparam Dim */ template class MvVecGenBase { public: /** * @brief returns the dimensions of vectors to be generated */ Index dims() const { return static_cast(*this).dims(); } /** * @brief generates multiple samples at once * * @tparam Urng * @param urng c++11-style random number generator * @param samples the number of samples to be generated * @return * a random matrix with a shape `(dim, samples)` which is consist of `samples` random vector columns */ template inline Matrix<_Scalar, Dim, -1> generate(Urng&& urng, Index samples) { return static_cast(*this).generatr(std::forward(urng), samples); } /** * @brief generates one sample * * @tparam Urng * @param urng c++11-style random number generator * @return a random vector with a shape `(dim,)` */ template inline Matrix<_Scalar, Dim, 1> generate(Urng&& urng) { return static_cast(*this).generatr(std::forward(urng)); } }; /** * @brief Base class of all multivariate random matrix generators * * @tparam DerivedGen * @tparam _Scalar * @tparam Dim */ template class MvMatGenBase { public: /** * @brief returns the dimensions of matrices to be generated */ Index dims() const { return static_cast(*this).dims(); } /** * @brief generates multiple samples at once * * @tparam Urng * @param urng c++11-style random number generator * @param samples the number of samples to be generated * @return * a random matrix with a shape `(dim, dim * samples)` which is `samples` random matrices concatenated along the column axis */ template inline Matrix<_Scalar, Dim, -1> generate(Urng&& urng, Index samples) { return static_cast(*this).generate(std::forward(urng), samples); } /** * @brief generates one sample * * @tparam Urng * @param urng c++11-style random number generator * @return a random matrix with a shape `(dim, dim)` */ template inline Matrix<_Scalar, Dim, Dim> generate(Urng&& urng) { return static_cast(*this).generate(std::forward(urng)); } }; template class CacheStore { protected: enum { max_size = sizeof(internal::find_best_packet::type) }; int8_t raw_data[max_size + _alignment - 1] = { 0, }; void* aligned_ptr; public: CacheStore() { aligned_ptr = (void*)((((size_t)raw_data + _alignment - 1) / _alignment) * _alignment); } CacheStore(const CacheStore& c) { std::copy(c.raw_data, c.raw_data + max_size, raw_data); aligned_ptr = (void*)((((size_t)raw_data + _alignment - 1) / _alignment) * _alignment); } CacheStore(CacheStore&& c) { std::copy(c.raw_data, c.raw_data + max_size, raw_data); aligned_ptr = (void*)((((size_t)raw_data + _alignment - 1) / _alignment) * _alignment); } template Ty& get() { return *(Ty*)aligned_ptr; } template const Ty& get() const { return *(const Ty*)aligned_ptr; } }; template<> class CacheStore<0> { protected: enum { max_size = sizeof(internal::find_best_packet::type) }; int8_t raw_data[max_size] = { 0, }; public: CacheStore() { } CacheStore(const CacheStore& c) { std::copy(c.raw_data, c.raw_data + max_size, raw_data); } CacheStore(CacheStore&& c) { std::copy(c.raw_data, c.raw_data + max_size, raw_data); } template Ty& get() { return *(Ty*)raw_data; } template const Ty& get() const { return *(const Ty*)raw_data; } }; using OptCacheStore = CacheStore; template struct ExtractFirstUint; template<> struct ExtractFirstUint { template auto operator()(Packet v) -> decltype(Eigen::internal::pfirst(v)) { return Eigen::internal::pfirst(v); } }; template<> struct ExtractFirstUint { template auto operator()(Packet v) -> uint64_t { uint64_t arr[sizeof(Packet) / 8]; Eigen::internal::pstoreu((Packet*)arr, v); return arr[0]; } }; /** * @brief Generator of random bits for integral scalars * * @tparam _Scalar any integral type */ template class RandbitsGen : public GenBase, _Scalar> { static_assert(std::is_integral<_Scalar>::value, "randBits needs integral types."); public: using Scalar = _Scalar; template EIGEN_STRONG_INLINE const _Scalar operator() (Rng&& rng) { using namespace Eigen::internal; return pfirst(std::forward(rng)()); } template EIGEN_STRONG_INLINE const Packet packetOp(Rng&& rng) { using namespace Eigen::internal; using RUtils = RawbitsMaker; return RUtils{}.rawbits(std::forward(rng)); } }; /** * @brief Generator of reals in a range `[-1, 1]` * * @tparam _Scalar any real type */ template class BalancedGen : public GenBase, _Scalar> { static_assert(std::is_floating_point<_Scalar>::value, "balanced needs floating point types."); public: using Scalar = _Scalar; template EIGEN_STRONG_INLINE const _Scalar operator() (Rng&& rng) { using namespace Eigen::internal; return ((_Scalar)((int32_t)pfirst(std::forward(rng)()) & 0x7FFFFFFF) / 0x7FFFFFFF) * 2 - 1; } template EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Packet packetOp(Rng&& rng) { using namespace Eigen::internal; using RUtils = RandUtils; return RUtils{}.balanced(std::forward(rng)); } }; /** * @brief Generator of reals in a range `[a, b]` * * @tparam _Scalar any real type */ template class Balanced2Gen : public GenBase, _Scalar> { static_assert(std::is_floating_point<_Scalar>::value, "balanced needs floating point types."); _Scalar slope = 2, bias = -1; public: using Scalar = _Scalar; /** * @brief Construct a new balanced generator * * @param _a,_b left and right boundary */ Balanced2Gen(_Scalar _a = -1, _Scalar _b = 1) : slope{ _b - _a }, bias{ _a } { } template EIGEN_STRONG_INLINE const _Scalar operator() (Rng&& rng) { using namespace Eigen::internal; return ((_Scalar)((int32_t)pfirst(std::forward(rng)()) & 0x7FFFFFFF) / 0x7FFFFFFF) * slope + bias; } template EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Packet packetOp(Rng&& rng) { using namespace Eigen::internal; using RUtils = RandUtils; return RUtils{}.balanced(std::forward(rng), slope, bias); } }; /** * @brief Generator of reals in a range `[0, 1)` * * @tparam _Scalar any real type */ template class StdUniformRealGen : public GenBase, _Scalar> { static_assert(std::is_floating_point<_Scalar>::value, "uniformReal needs floating point types."); public: using Scalar = _Scalar; template EIGEN_STRONG_INLINE const _Scalar operator() (Rng&& rng) { using namespace Eigen::internal; return BitScalar<_Scalar>{}.to_ur(ExtractFirstUint<_Scalar>{}(std::forward(rng)())); } template EIGEN_STRONG_INLINE const _Scalar nzur_scalar(Rng&& rng) { using namespace Eigen::internal; return BitScalar<_Scalar>{}.to_nzur(ExtractFirstUint<_Scalar>{}(std::forward(rng)())); } template EIGEN_STRONG_INLINE const Packet packetOp(Rng&& rng) { using namespace Eigen::internal; using RUtils = RandUtils; return RUtils{}.uniform_real(std::forward(rng)); } }; /** * @brief Generator of reals in a range `[a, b)` * * @tparam _Scalar any real type */ template class UniformRealGen : public GenBase, _Scalar> { static_assert(std::is_floating_point<_Scalar>::value, "uniformReal needs floating point types."); _Scalar bias, slope; public: using Scalar = _Scalar; UniformRealGen(_Scalar _min = 0, _Scalar _max = 1) : bias{ _min }, slope{ _max - _min } { } UniformRealGen(const UniformRealGen&) = default; UniformRealGen(UniformRealGen&&) = default; UniformRealGen& operator=(const UniformRealGen&) = default; UniformRealGen& operator=(UniformRealGen&&) = default; template EIGEN_STRONG_INLINE const _Scalar operator() (Rng&& rng) { using namespace Eigen::internal; return bias + BitScalar<_Scalar>{}.to_ur(pfirst(std::forward(rng)())) * slope; } template EIGEN_STRONG_INLINE const Packet packetOp(Rng&& rng) { using namespace Eigen::internal; using RUtils = RandUtils; return padd(pmul( RUtils{}.uniform_real(std::forward(rng)), pset1(slope) ), pset1(bias)); } }; /** * @brief Generator of Bernoulli distribution * * @tparam _Scalar */ template class BernoulliGen : public GenBase, _Scalar> { uint32_t p; public: using Scalar = _Scalar; BernoulliGen(double _p = 0.5) { eigen_assert(0 <= _p && _p <= 1 ); p = (uint32_t)(_p * 0x80000000); } BernoulliGen(const BernoulliGen&) = default; BernoulliGen(BernoulliGen&&) = default; BernoulliGen& operator=(const BernoulliGen&) = default; BernoulliGen& operator=(BernoulliGen&&) = default; template EIGEN_STRONG_INLINE const _Scalar operator() (Rng&& rng) { using namespace Eigen::internal; return (((uint32_t)pfirst(std::forward(rng)()) & 0x7FFFFFFF) < p) ? 1 : 0; } template EIGEN_STRONG_INLINE const Packet packetOp(Rng&& rng) { using namespace Eigen::internal; using IPacket = decltype(reinterpret_to_int(std::declval())); using RUtils = RawbitsMaker; auto one = pset1(1); auto zero = pset1(0); auto r = RUtils{}.rawbits(std::forward(rng)); r = pand(r, pset1(0x7FFFFFFF)); return pblendv(pcmplt(r, pset1(p)), one, zero); } }; template using RandBitsType = CwiseNullaryOp, typename Derived::Scalar, Urng, true>, const Derived>; /** * @brief generates integers with random bits * * @tparam Derived * @tparam Urng * @param rows the number of rows being generated * @param cols the number of columns being generated * @param urng c++11-style random number generator * @return a random matrix expression with a shape (`rows`, `cols`) * * @see Eigen::Rand::RandbitsGen */ template inline const RandBitsType randBits(Index rows, Index cols, Urng&& urng) { return { rows, cols, { std::forward(urng) } }; } /** * @brief generates integers with random bits * * @tparam Derived * @tparam Urng * @param o an instance of any type of Eigen::DenseBase * @param urng c++11-style random number generator * @return a random matrix expression of the same shape as `o` * * @see Eigen::Rand::RandbitsGen */ template inline const RandBitsType randBitsLike(Derived& o, Urng&& urng) { return { o.rows(), o.cols(), { std::forward(urng) } }; } template using BalancedType = CwiseNullaryOp, typename Derived::Scalar, Urng, true>, const Derived>; /** * @brief generates reals in a range `[-1, 1]` * * @tparam Derived a type of Eigen::DenseBase * @tparam Urng * @param rows the number of rows being generated * @param cols the number of columns being generated * @param urng c++11-style random number generator * @return a random matrix expression with a shape (`rows`, `cols`) * * @see Eigen::Rand::BalancedGen */ template inline const BalancedType balanced(Index rows, Index cols, Urng&& urng) { return { rows, cols, { std::forward(urng) } }; } /** * @brief generates reals in a range `[-1, 1]` * * @tparam Derived * @tparam Urng * @param o an instance of any type of Eigen::DenseBase * @param urng c++11-style random number generator * @return a random matrix expression of the same shape as `o` * * @see Eigen::Rand::BalancedGen */ template inline const BalancedType balancedLike(const Derived& o, Urng&& urng) { return { o.rows(), o.cols(), { std::forward(urng) } }; } template using Balanced2Type = CwiseNullaryOp, typename Derived::Scalar, Urng, true>, const Derived>; /** * @brief generates reals in a range `[a, b]` * * @tparam Derived a type of Eigen::DenseBase * @tparam Urng * @param rows the number of rows being generated * @param cols the number of columns being generated * @param urng c++11-style random number generator * @param a,b left and right boundary * @return a random matrix expression with a shape (`rows`, `cols`) * * @see Eigen::Rand::BalancedGen */ template inline const Balanced2Type balanced(Index rows, Index cols, Urng&& urng, typename Derived::Scalar a, typename Derived::Scalar b) { return { rows, cols, { std::forward(urng), Balanced2Gen{a, b} } }; } /** * @brief generates reals in a range `[a, b]` * * @tparam Derived * @tparam Urng * @param o an instance of any type of Eigen::DenseBase * @param urng c++11-style random number generator * @param a,b left and right boundary * @return a random matrix expression of the same shape as `o` * * @see Eigen::Rand::BalancedGen */ template inline const Balanced2Type balancedLike(const Derived& o, Urng&& urng, typename Derived::Scalar a, typename Derived::Scalar b) { return { o.rows(), o.cols(), { std::forward(urng), Balanced2Gen{a, b} } }; } template using StdUniformRealType = CwiseNullaryOp, typename Derived::Scalar, Urng, true>, const Derived>; /** * @brief generates reals in a range `[0, 1)` * * @tparam Derived a type of Eigen::DenseBase * @tparam Urng * @param rows the number of rows being generated * @param cols the number of columns being generated * @param urng c++11-style random number generator * @return a random matrix expression with a shape (`rows`, `cols`) * * @see Eigen::Rand::UniformRealGen */ template inline const StdUniformRealType uniformReal(Index rows, Index cols, Urng&& urng) { return { rows, cols, { std::forward(urng) } }; } /** * @brief generates reals in a range `[0, 1)` * * @tparam Derived * @tparam Urng * @param o an instance of any type of Eigen::DenseBase * @param urng c++11-style random number generator * @return a random matrix expression of the same shape as `o` * * @see Eigen::Rand::UniformRealGen */ template inline const StdUniformRealType uniformRealLike(Derived& o, Urng&& urng) { return { o.rows(), o.cols(), { std::forward(urng) } }; } template using UniformRealType = CwiseNullaryOp, typename Derived::Scalar, Urng, true>, const Derived>; /** * @brief generates reals in a range `[min, max)` * * @tparam Derived a type of Eigen::DenseBase * @tparam Urng * @param rows the number of rows being generated * @param cols the number of columns being generated * @param urng c++11-style random number generator * @param min, max the range of reals being generated * @return a random matrix expression with a shape (`rows`, `cols`) * * @see Eigen::Rand::UniformRealGen */ template inline const UniformRealType uniformReal(Index rows, Index cols, Urng&& urng, typename Derived::Scalar min, typename Derived::Scalar max) { return { rows, cols, { std::forward(urng), UniformRealGen{ min, max } } }; } /** * @brief generates reals in a range `[min, max)` * * @tparam Derived * @tparam Urng * @param o an instance of any type of Eigen::DenseBase * @param urng c++11-style random number generator * @param min, max the range of reals being generated * @return a random matrix expression of the same shape as `o` * * @see Eigen::Rand::UniformRealGen */ template inline const UniformRealType uniformRealLike(Derived& o, Urng&& urng, typename Derived::Scalar min, typename Derived::Scalar max) { return { o.rows(), o.cols(), { std::forward(urng), UniformRealGen{ min, max } } }; } template using BernoulliType = CwiseNullaryOp, typename Derived::Scalar, Urng, true>, const Derived>; /** * @brief generates 1 with probability `p` and 0 with probability `1 - p` * * @tparam Derived * @tparam Urng * @param rows the number of rows being generated * @param cols the number of columns being generated * @param urng c++11-style random number generator * @param p a probability of generating 1 * @return a random matrix expression with a shape (`rows`, `cols`) */ template inline const BernoulliType bernoulli(Index rows, Index cols, Urng&& urng, double p = 0.5) { return { rows, cols, { std::forward(urng), BernoulliGen{ p } } }; } /** * @brief generates 1 with probability `p` and 0 with probability `1 - p` * * @tparam Derived * @tparam Urng * @param o an instance of any type of Eigen::DenseBase * @param urng c++11-style random number generator * @param p a probability of generating 1 * @return a random matrix expression of the same shape as `o` */ template inline const BernoulliType bernoulli(Derived& o, Urng&& urng, double p = 0.5) { return { o.rows(), o.cols(), { std::forward(urng), BernoulliGen{ p } } }; } } } #endif