mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 19:40:18 +00:00
1008 lines
40 KiB
C++
1008 lines
40 KiB
C++
/***************************************************************************
|
|
* Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht *
|
|
* Copyright (c) QuantStack *
|
|
* *
|
|
* Distributed under the terms of the BSD 3-Clause License. *
|
|
* *
|
|
* The full license is in the file LICENSE, distributed with this software. *
|
|
****************************************************************************/
|
|
|
|
/**
|
|
* @brief functions to obtain xgenerators generating random numbers with given shape
|
|
*/
|
|
|
|
#ifndef XTENSOR_RANDOM_HPP
|
|
#define XTENSOR_RANDOM_HPP
|
|
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include <random>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#include <xtl/xspan.hpp>
|
|
|
|
#include "xbuilder.hpp"
|
|
#include "xgenerator.hpp"
|
|
#include "xindex_view.hpp"
|
|
#include "xmath.hpp"
|
|
#include "xtensor.hpp"
|
|
#include "xtensor_config.hpp"
|
|
#include "xview.hpp"
|
|
|
|
namespace xt
|
|
{
|
|
/*********************
|
|
* Random generators *
|
|
*********************/
|
|
|
|
namespace random
|
|
{
|
|
using default_engine_type = std::mt19937;
|
|
using seed_type = default_engine_type::result_type;
|
|
|
|
default_engine_type& get_default_random_engine();
|
|
void seed(seed_type seed);
|
|
|
|
template <class T, class S, class E = random::default_engine_type>
|
|
auto rand(const S& shape, T lower = 0, T upper = 1, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class S, class E = random::default_engine_type>
|
|
auto randint(
|
|
const S& shape,
|
|
T lower = 0,
|
|
T upper = (std::numeric_limits<T>::max)(),
|
|
E& engine = random::get_default_random_engine()
|
|
);
|
|
|
|
template <class T, class S, class E = random::default_engine_type>
|
|
auto randn(const S& shape, T mean = 0, T std_dev = 1, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class S, class D = double, class E = random::default_engine_type>
|
|
auto
|
|
binomial(const S& shape, T trials = 1, D prob = 0.5, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class S, class D = double, class E = random::default_engine_type>
|
|
auto geometric(const S& shape, D prob = 0.5, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class S, class D = double, class E = random::default_engine_type>
|
|
auto
|
|
negative_binomial(const S& shape, T k = 1, D prob = 0.5, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class S, class D = double, class E = random::default_engine_type>
|
|
auto poisson(const S& shape, D rate = 1.0, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class S, class E = random::default_engine_type>
|
|
auto exponential(const S& shape, T rate = 1.0, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class S, class E = random::default_engine_type>
|
|
auto
|
|
gamma(const S& shape, T alpha = 1.0, T beta = 1.0, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class S, class E = random::default_engine_type>
|
|
auto weibull(const S& shape, T a = 1.0, T b = 1.0, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class S, class E = random::default_engine_type>
|
|
auto
|
|
extreme_value(const S& shape, T a = 0.0, T b = 1.0, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class S, class E = random::default_engine_type>
|
|
auto
|
|
lognormal(const S& shape, T mean = 0, T std_dev = 1, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class S, class E = random::default_engine_type>
|
|
auto chi_squared(const S& shape, T deg = 1.0, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class S, class E = random::default_engine_type>
|
|
auto cauchy(const S& shape, T a = 0.0, T b = 1.0, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class S, class E = random::default_engine_type>
|
|
auto fisher_f(const S& shape, T m = 1.0, T n = 1.0, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class S, class E = random::default_engine_type>
|
|
auto student_t(const S& shape, T n = 1.0, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class I, std::size_t L, class E = random::default_engine_type>
|
|
auto
|
|
rand(const I (&shape)[L], T lower = 0, T upper = 1, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class I, std::size_t L, class E = random::default_engine_type>
|
|
auto randint(
|
|
const I (&shape)[L],
|
|
T lower = 0,
|
|
T upper = (std::numeric_limits<T>::max)(),
|
|
E& engine = random::get_default_random_engine()
|
|
);
|
|
|
|
template <class T, class I, std::size_t L, class E = random::default_engine_type>
|
|
auto
|
|
randn(const I (&shape)[L], T mean = 0, T std_dev = 1, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class I, std::size_t L, class D = double, class E = random::default_engine_type>
|
|
auto
|
|
binomial(const I (&shape)[L], T trials = 1, D prob = 0.5, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class I, std::size_t L, class D = double, class E = random::default_engine_type>
|
|
auto geometric(const I (&shape)[L], D prob = 0.5, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class I, std::size_t L, class D = double, class E = random::default_engine_type>
|
|
auto negative_binomial(
|
|
const I (&shape)[L],
|
|
T k = 1,
|
|
D prob = 0.5,
|
|
E& engine = random::get_default_random_engine()
|
|
);
|
|
|
|
template <class T, class I, std::size_t L, class D = double, class E = random::default_engine_type>
|
|
auto poisson(const I (&shape)[L], D rate = 1.0, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class I, std::size_t L, class E = random::default_engine_type>
|
|
auto exponential(const I (&shape)[L], T rate = 1.0, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class I, std::size_t L, class E = random::default_engine_type>
|
|
auto
|
|
gamma(const I (&shape)[L], T alpha = 1.0, T beta = 1.0, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class I, std::size_t L, class E = random::default_engine_type>
|
|
auto
|
|
weibull(const I (&shape)[L], T a = 1.0, T b = 1.0, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class I, std::size_t L, class E = random::default_engine_type>
|
|
auto
|
|
extreme_value(const I (&shape)[L], T a = 0.0, T b = 1.0, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class I, std::size_t L, class E = random::default_engine_type>
|
|
auto lognormal(
|
|
const I (&shape)[L],
|
|
T mean = 0.0,
|
|
T std_dev = 1.0,
|
|
E& engine = random::get_default_random_engine()
|
|
);
|
|
|
|
template <class T, class I, std::size_t L, class E = random::default_engine_type>
|
|
auto chi_squared(const I (&shape)[L], T deg = 1.0, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class I, std::size_t L, class E = random::default_engine_type>
|
|
auto cauchy(const I (&shape)[L], T a = 0.0, T b = 1.0, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class I, std::size_t L, class E = random::default_engine_type>
|
|
auto
|
|
fisher_f(const I (&shape)[L], T m = 1.0, T n = 1.0, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class I, std::size_t L, class E = random::default_engine_type>
|
|
auto student_t(const I (&shape)[L], T n = 1.0, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class E = random::default_engine_type>
|
|
void shuffle(xexpression<T>& e, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class E = random::default_engine_type>
|
|
std::enable_if_t<xtl::is_integral<T>::value, xtensor<T, 1>>
|
|
permutation(T e, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class E = random::default_engine_type>
|
|
std::enable_if_t<is_xexpression<std::decay_t<T>>::value, std::decay_t<T>>
|
|
permutation(T&& e, E& engine = random::get_default_random_engine());
|
|
|
|
template <class T, class E = random::default_engine_type>
|
|
xtensor<typename T::value_type, 1> choice(
|
|
const xexpression<T>& e,
|
|
std::size_t n,
|
|
bool replace = true,
|
|
E& engine = random::get_default_random_engine()
|
|
);
|
|
|
|
template <class T, class W, class E = random::default_engine_type>
|
|
xtensor<typename T::value_type, 1> choice(
|
|
const xexpression<T>& e,
|
|
std::size_t n,
|
|
const xexpression<W>& weights,
|
|
bool replace = true,
|
|
E& engine = random::get_default_random_engine()
|
|
);
|
|
}
|
|
|
|
namespace detail
|
|
{
|
|
template <class T, class E, class D>
|
|
struct random_impl
|
|
{
|
|
using value_type = T;
|
|
|
|
random_impl(E& engine, D&& dist)
|
|
: m_engine(engine)
|
|
, m_dist(std::move(dist))
|
|
{
|
|
}
|
|
|
|
template <class... Args>
|
|
inline value_type operator()(Args...) const
|
|
{
|
|
return m_dist(m_engine);
|
|
}
|
|
|
|
template <class It>
|
|
inline value_type element(It, It) const
|
|
{
|
|
return m_dist(m_engine);
|
|
}
|
|
|
|
template <class EX>
|
|
inline void assign_to(xexpression<EX>& e) const noexcept
|
|
{
|
|
// Note: we're not going row/col major here
|
|
auto& ed = e.derived_cast();
|
|
for (auto&& el : ed.storage())
|
|
{
|
|
el = m_dist(m_engine);
|
|
}
|
|
}
|
|
|
|
private:
|
|
|
|
E& m_engine;
|
|
mutable D m_dist;
|
|
};
|
|
}
|
|
|
|
namespace random
|
|
{
|
|
/**
|
|
* Returns a reference to the default random number engine
|
|
*/
|
|
inline default_engine_type& get_default_random_engine()
|
|
{
|
|
static default_engine_type mt;
|
|
return mt;
|
|
}
|
|
|
|
/**
|
|
* Seeds the default random number generator with @p seed
|
|
* @param seed The seed
|
|
*/
|
|
inline void seed(seed_type seed)
|
|
{
|
|
get_default_random_engine().seed(seed);
|
|
}
|
|
|
|
/**
|
|
* xexpression with specified @p shape containing uniformly distributed random numbers
|
|
* in the interval from @p lower to @p upper, excluding upper.
|
|
*
|
|
* Numbers are drawn from @c std::uniform_real_distribution.
|
|
*
|
|
* @param shape shape of resulting xexpression
|
|
* @param lower lower bound
|
|
* @param upper upper bound
|
|
* @param engine random number engine
|
|
* @tparam T number type to use
|
|
*/
|
|
template <class T, class S, class E>
|
|
inline auto rand(const S& shape, T lower, T upper, E& engine)
|
|
{
|
|
std::uniform_real_distribution<T> dist(lower, upper);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
/**
|
|
* xexpression with specified @p shape containing uniformly distributed
|
|
* random integers in the interval from @p lower to @p upper, excluding upper.
|
|
*
|
|
* Numbers are drawn from @c std::uniform_int_distribution.
|
|
*
|
|
* @param shape shape of resulting xexpression
|
|
* @param lower lower bound
|
|
* @param upper upper bound
|
|
* @param engine random number engine
|
|
* @tparam T number type to use
|
|
*/
|
|
template <class T, class S, class E>
|
|
inline auto randint(const S& shape, T lower, T upper, E& engine)
|
|
{
|
|
std::uniform_int_distribution<T> dist(lower, T(upper - 1));
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
/**
|
|
* xexpression with specified @p shape containing numbers sampled from
|
|
* the Normal (Gaussian) random number distribution with mean @p mean and
|
|
* standard deviation @p std_dev.
|
|
*
|
|
* Numbers are drawn from @c std::normal_distribution.
|
|
*
|
|
* @param shape shape of resulting xexpression
|
|
* @param mean mean of normal distribution
|
|
* @param std_dev standard deviation of normal distribution
|
|
* @param engine random number engine
|
|
* @tparam T number type to use
|
|
*/
|
|
template <class T, class S, class E>
|
|
inline auto randn(const S& shape, T mean, T std_dev, E& engine)
|
|
{
|
|
std::normal_distribution<T> dist(mean, std_dev);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
/**
|
|
* xexpression with specified @p shape containing numbers sampled from
|
|
* the binomial random number distribution for @p trials trials with
|
|
* probability of success equal to @p prob.
|
|
*
|
|
* Numbers are drawn from @c std::binomial_distribution.
|
|
*
|
|
* @param shape shape of resulting xexpression
|
|
* @param trials number of Bernoulli trials
|
|
* @param prob probability of success of each trial
|
|
* @param engine random number engine
|
|
* @tparam T number type to use
|
|
*/
|
|
template <class T, class S, class D, class E>
|
|
inline auto binomial(const S& shape, T trials, D prob, E& engine)
|
|
{
|
|
std::binomial_distribution<T> dist(trials, prob);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
/**
|
|
* xexpression with specified @p shape containing numbers sampled from
|
|
* a gemoetric random number distribution with
|
|
* probability of success equal to @p prob for each of the Bernoulli trials.
|
|
*
|
|
* Numbers are drawn from @c std::geometric_distribution.
|
|
*
|
|
* @param shape shape of resulting xexpression
|
|
* @param prob probability of success of each trial
|
|
* @param engine random number engine
|
|
* @tparam T number type to use
|
|
*/
|
|
template <class T, class S, class D, class E>
|
|
inline auto geometric(const S& shape, D prob, E& engine)
|
|
{
|
|
std::geometric_distribution<T> dist(prob);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
/**
|
|
* xexpression with specified @p shape containing numbers sampled from
|
|
* a negative binomial random number distribution (also known as Pascal distribution)
|
|
* that returns the number of successes before @p k trials with probability of success
|
|
* equal to @p prob for each of the Bernoulli trials.
|
|
*
|
|
* Numbers are drawn from @c std::negative_binomial_distribution.
|
|
*
|
|
* @param shape shape of resulting xexpression
|
|
* @param k number of unsuccessful trials
|
|
* @param prob probability of success of each trial
|
|
* @param engine random number engine
|
|
* @tparam T number type to use
|
|
*/
|
|
template <class T, class S, class D, class E>
|
|
inline auto negative_binomial(const S& shape, T k, D prob, E& engine)
|
|
{
|
|
std::negative_binomial_distribution<T> dist(k, prob);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
/**
|
|
* xexpression with specified @p shape containing numbers sampled from
|
|
* a Poisson random number distribution with rate @p rate
|
|
*
|
|
* Numbers are drawn from @c std::poisson_distribution.
|
|
*
|
|
* @param shape shape of resulting xexpression
|
|
* @param rate rate of Poisson distribution
|
|
* @param engine random number engine
|
|
* @tparam T number type to use
|
|
*/
|
|
template <class T, class S, class D, class E>
|
|
inline auto poisson(const S& shape, D rate, E& engine)
|
|
{
|
|
std::poisson_distribution<T> dist(rate);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
/**
|
|
* xexpression with specified @p shape containing numbers sampled from
|
|
* a exponential random number distribution with rate @p rate
|
|
*
|
|
* Numbers are drawn from @c std::exponential_distribution.
|
|
*
|
|
* @param shape shape of resulting xexpression
|
|
* @param rate rate of exponential distribution
|
|
* @param engine random number engine
|
|
* @tparam T number type to use
|
|
*/
|
|
template <class T, class S, class E>
|
|
inline auto exponential(const S& shape, T rate, E& engine)
|
|
{
|
|
std::exponential_distribution<T> dist(rate);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
/**
|
|
* xexpression with specified @p shape containing numbers sampled from
|
|
* a gamma random number distribution with shape @p alpha and scale @p beta
|
|
*
|
|
* Numbers are drawn from @c std::gamma_distribution.
|
|
*
|
|
* @param shape shape of resulting xexpression
|
|
* @param alpha shape of the gamma distribution
|
|
* @param beta scale of the gamma distribution
|
|
* @param engine random number engine
|
|
* @tparam T number type to use
|
|
*/
|
|
template <class T, class S, class E>
|
|
inline auto gamma(const S& shape, T alpha, T beta, E& engine)
|
|
{
|
|
std::gamma_distribution<T> dist(alpha, beta);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
/**
|
|
* xexpression with specified @p shape containing numbers sampled from
|
|
* a Weibull random number distribution with shape @p a and scale @p b
|
|
*
|
|
* Numbers are drawn from @c std::weibull_distribution.
|
|
*
|
|
* @param shape shape of resulting xexpression
|
|
* @param a shape of the weibull distribution
|
|
* @param b scale of the weibull distribution
|
|
* @param engine random number engine
|
|
* @tparam T number type to use
|
|
*/
|
|
template <class T, class S, class E>
|
|
inline auto weibull(const S& shape, T a, T b, E& engine)
|
|
{
|
|
std::weibull_distribution<T> dist(a, b);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
/**
|
|
* xexpression with specified @p shape containing numbers sampled from
|
|
* a extreme value random number distribution with shape @p a and scale @p b
|
|
*
|
|
* Numbers are drawn from @c std::extreme_value_distribution.
|
|
*
|
|
* @param shape shape of resulting xexpression
|
|
* @param a shape of the extreme value distribution
|
|
* @param b scale of the extreme value distribution
|
|
* @param engine random number engine
|
|
* @tparam T number type to use
|
|
*/
|
|
template <class T, class S, class E>
|
|
inline auto extreme_value(const S& shape, T a, T b, E& engine)
|
|
{
|
|
std::extreme_value_distribution<T> dist(a, b);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
/**
|
|
* xexpression with specified @p shape containing numbers sampled from
|
|
* the Log-Normal random number distribution with mean @p mean and
|
|
* standard deviation @p std_dev.
|
|
*
|
|
* Numbers are drawn from @c std::lognormal_distribution.
|
|
*
|
|
* @param shape shape of resulting xexpression
|
|
* @param mean mean of normal distribution
|
|
* @param std_dev standard deviation of normal distribution
|
|
* @param engine random number engine
|
|
* @tparam T number type to use
|
|
*/
|
|
template <class T, class S, class E>
|
|
inline auto lognormal(const S& shape, T mean, T std_dev, E& engine)
|
|
{
|
|
std::lognormal_distribution<T> dist(mean, std_dev);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
/**
|
|
* xexpression with specified @p shape containing numbers sampled from
|
|
* the chi-squared random number distribution with @p deg degrees of freedom.
|
|
*
|
|
* Numbers are drawn from @c std::chi_squared_distribution.
|
|
*
|
|
* @param shape shape of resulting xexpression
|
|
* @param deg degrees of freedom
|
|
* @param engine random number engine
|
|
* @tparam T number type to use
|
|
*/
|
|
template <class T, class S, class E>
|
|
inline auto chi_squared(const S& shape, T deg, E& engine)
|
|
{
|
|
std::chi_squared_distribution<T> dist(deg);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
/**
|
|
* xexpression with specified @p shape containing numbers sampled from
|
|
* a Cauchy random number distribution with peak @p a and scale @p b
|
|
*
|
|
* Numbers are drawn from @c std::cauchy_distribution.
|
|
*
|
|
* @param shape shape of resulting xexpression
|
|
* @param a peak of the Cauchy distribution
|
|
* @param b scale of the Cauchy distribution
|
|
* @param engine random number engine
|
|
* @tparam T number type to use
|
|
*/
|
|
template <class T, class S, class E>
|
|
inline auto cauchy(const S& shape, T a, T b, E& engine)
|
|
{
|
|
std::cauchy_distribution<T> dist(a, b);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
/**
|
|
* xexpression with specified @p shape containing numbers sampled from
|
|
* a Fisher-f random number distribution with numerator degrees of
|
|
* freedom equal to @p m and denominator degrees of freedom equal to @p n
|
|
*
|
|
* Numbers are drawn from @c std::fisher_f_distribution.
|
|
*
|
|
* @param shape shape of resulting xexpression
|
|
* @param m numerator degrees of freedom
|
|
* @param n denominator degrees of freedom
|
|
* @param engine random number engine
|
|
* @tparam T number type to use
|
|
*/
|
|
template <class T, class S, class E>
|
|
inline auto fisher_f(const S& shape, T m, T n, E& engine)
|
|
{
|
|
std::fisher_f_distribution<T> dist(m, n);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
/**
|
|
* xexpression with specified @p shape containing numbers sampled from
|
|
* a Student-t random number distribution with degrees of
|
|
* freedom equal to @p n
|
|
*
|
|
* Numbers are drawn from @c std::student_t_distribution.
|
|
*
|
|
* @param shape shape of resulting xexpression
|
|
* @param n degrees of freedom
|
|
* @param engine random number engine
|
|
* @tparam T number type to use
|
|
*/
|
|
template <class T, class S, class E>
|
|
inline auto student_t(const S& shape, T n, E& engine)
|
|
{
|
|
std::student_t_distribution<T> dist(n);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
template <class T, class I, std::size_t L, class E>
|
|
inline auto rand(const I (&shape)[L], T lower, T upper, E& engine)
|
|
{
|
|
std::uniform_real_distribution<T> dist(lower, upper);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
template <class T, class I, std::size_t L, class E>
|
|
inline auto randint(const I (&shape)[L], T lower, T upper, E& engine)
|
|
{
|
|
std::uniform_int_distribution<T> dist(lower, T(upper - 1));
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
template <class T, class I, std::size_t L, class E>
|
|
inline auto randn(const I (&shape)[L], T mean, T std_dev, E& engine)
|
|
{
|
|
std::normal_distribution<T> dist(mean, std_dev);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
template <class T, class I, std::size_t L, class D, class E>
|
|
inline auto binomial(const I (&shape)[L], T trials, D prob, E& engine)
|
|
{
|
|
std::binomial_distribution<T> dist(trials, prob);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
template <class T, class I, std::size_t L, class D, class E>
|
|
inline auto geometric(const I (&shape)[L], D prob, E& engine)
|
|
{
|
|
std::geometric_distribution<T> dist(prob);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
template <class T, class I, std::size_t L, class D, class E>
|
|
inline auto negative_binomial(const I (&shape)[L], T k, D prob, E& engine)
|
|
{
|
|
std::negative_binomial_distribution<T> dist(k, prob);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
template <class T, class I, std::size_t L, class D, class E>
|
|
inline auto poisson(const I (&shape)[L], D rate, E& engine)
|
|
{
|
|
std::poisson_distribution<T> dist(rate);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
template <class T, class I, std::size_t L, class E>
|
|
inline auto exponential(const I (&shape)[L], T rate, E& engine)
|
|
{
|
|
std::exponential_distribution<T> dist(rate);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
template <class T, class I, std::size_t L, class E>
|
|
inline auto gamma(const I (&shape)[L], T alpha, T beta, E& engine)
|
|
{
|
|
std::gamma_distribution<T> dist(alpha, beta);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
template <class T, class I, std::size_t L, class E>
|
|
inline auto weibull(const I (&shape)[L], T a, T b, E& engine)
|
|
{
|
|
std::weibull_distribution<T> dist(a, b);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
template <class T, class I, std::size_t L, class E>
|
|
inline auto extreme_value(const I (&shape)[L], T a, T b, E& engine)
|
|
{
|
|
std::extreme_value_distribution<T> dist(a, b);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
template <class T, class I, std::size_t L, class E>
|
|
inline auto lognormal(const I (&shape)[L], T mean, T std_dev, E& engine)
|
|
{
|
|
std::lognormal_distribution<T> dist(mean, std_dev);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
template <class T, class I, std::size_t L, class E>
|
|
inline auto chi_squared(const I (&shape)[L], T deg, E& engine)
|
|
{
|
|
std::chi_squared_distribution<T> dist(deg);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
template <class T, class I, std::size_t L, class E>
|
|
inline auto cauchy(const I (&shape)[L], T a, T b, E& engine)
|
|
{
|
|
std::cauchy_distribution<T> dist(a, b);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
template <class T, class I, std::size_t L, class E>
|
|
inline auto fisher_f(const I (&shape)[L], T m, T n, E& engine)
|
|
{
|
|
std::fisher_f_distribution<T> dist(m, n);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
template <class T, class I, std::size_t L, class E>
|
|
inline auto student_t(const I (&shape)[L], T n, E& engine)
|
|
{
|
|
std::student_t_distribution<T> dist(n);
|
|
return detail::make_xgenerator(
|
|
detail::random_impl<T, E, decltype(dist)>(engine, std::move(dist)),
|
|
shape
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Randomly shuffle elements inplace in xcontainer along first axis.
|
|
* The order of sub-arrays is changed but their contents remain the same.
|
|
*
|
|
* @param e xcontainer to shuffle inplace
|
|
* @param engine random number engine
|
|
*/
|
|
template <class T, class E>
|
|
void shuffle(xexpression<T>& e, E& engine)
|
|
{
|
|
T& de = e.derived_cast();
|
|
|
|
if (de.dimension() == 1)
|
|
{
|
|
using size_type = typename T::size_type;
|
|
auto first = de.begin();
|
|
auto last = de.end();
|
|
|
|
for (size_type i = std::size_t((last - first) - 1); i > 0; --i)
|
|
{
|
|
std::uniform_int_distribution<size_type> dist(0, i);
|
|
auto j = dist(engine);
|
|
using std::swap;
|
|
swap(first[i], first[j]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
using size_type = typename T::size_type;
|
|
decltype(auto) buf = empty_like(view(de, 0));
|
|
|
|
for (size_type i = de.shape()[0] - 1; i > 0; --i)
|
|
{
|
|
std::uniform_int_distribution<size_type> dist(0, i);
|
|
size_type j = dist(engine);
|
|
|
|
buf = view(de, j);
|
|
view(de, j) = view(de, i);
|
|
view(de, i) = buf;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Randomly permute a sequence, or return a permuted range.
|
|
*
|
|
* If the first parameter is an integer, this function creates a new
|
|
* ``arange(e)`` and returns it randomly permuted. Otherwise, this
|
|
* function creates a copy of the input, passes it to @sa shuffle and
|
|
* returns the result.
|
|
*
|
|
* @param e input xexpression or integer
|
|
* @param engine random number engine to use (optional)
|
|
*
|
|
* @return randomly permuted copy of container or arange.
|
|
*/
|
|
template <class T, class E>
|
|
std::enable_if_t<xtl::is_integral<T>::value, xtensor<T, 1>> permutation(T e, E& engine)
|
|
{
|
|
xt::xtensor<T, 1> res = xt::arange<T>(e);
|
|
shuffle(res, engine);
|
|
return res;
|
|
}
|
|
|
|
/// @cond DOXYGEN_INCLUDE_SFINAE
|
|
template <class T, class E>
|
|
std::enable_if_t<is_xexpression<std::decay_t<T>>::value, std::decay_t<T>> permutation(T&& e, E& engine)
|
|
{
|
|
using copy_type = std::decay_t<T>;
|
|
copy_type res = e;
|
|
shuffle(res, engine);
|
|
return res;
|
|
}
|
|
|
|
/// @endcond
|
|
|
|
/**
|
|
* Randomly select n unique elements from xexpression e.
|
|
* Note: this function makes a copy of your data, and only 1D data is accepted.
|
|
*
|
|
* @param e expression to sample from
|
|
* @param n number of elements to sample
|
|
* @param replace whether to sample with or without replacement
|
|
* @param engine random number engine
|
|
*
|
|
* @return xtensor containing 1D container of sampled elements
|
|
*/
|
|
template <class T, class E>
|
|
xtensor<typename T::value_type, 1>
|
|
choice(const xexpression<T>& e, std::size_t n, bool replace, E& engine)
|
|
{
|
|
const auto& de = e.derived_cast();
|
|
XTENSOR_ASSERT((de.dimension() == 1));
|
|
XTENSOR_ASSERT((replace || n <= de.size()));
|
|
using result_type = xtensor<typename T::value_type, 1>;
|
|
using size_type = typename result_type::size_type;
|
|
result_type result;
|
|
result.resize({n});
|
|
|
|
if (replace)
|
|
{
|
|
auto dist = std::uniform_int_distribution<size_type>(0, de.size() - 1);
|
|
for (size_type i = 0; i < n; ++i)
|
|
{
|
|
result[i] = de.storage()[dist(engine)];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Naive resevoir sampling without weighting:
|
|
std::copy(de.storage().begin(), de.storage().begin() + n, result.begin());
|
|
size_type i = n;
|
|
for (auto it = de.storage().begin() + n; it != de.storage().end(); ++it, ++i)
|
|
{
|
|
auto idx = std::uniform_int_distribution<size_type>(0, i)(engine);
|
|
if (idx < n)
|
|
{
|
|
result.storage()[idx] = *it;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Weighted random sampling.
|
|
*
|
|
* Randomly sample n unique elements from xexpression ``e`` using the discrete distribution
|
|
* parametrized by the weights ``w``. When sampling with replacement, this means that the probability
|
|
* to sample element ``e[i]`` is defined as
|
|
* ``w[i] / sum(w)``.
|
|
* Without replacement, this only describes the probability of the first sample element.
|
|
* In successive samples, the weight of items already sampled is assumed to be zero.
|
|
*
|
|
* For weighted random sampling with replacement, binary search with cumulative weights alogrithm is
|
|
* used. For weighted random sampling without replacement, the algorithm used is the exponential sort
|
|
* from [Efraimidis and Spirakis](https://doi.org/10.1016/j.ipl.2005.11.003) (2006) with the ``weight
|
|
* / randexp(1)`` [trick](https://web.archive.org/web/20201021162211/https://krlmlr.github.io/wrswoR/)
|
|
* from Kirill Müller.
|
|
*
|
|
* Note: this function makes a copy of your data, and only 1D data is accepted.
|
|
*
|
|
* @param e expression to sample from
|
|
* @param n number of elements to sample
|
|
* @param w expression for the weight distribution.
|
|
* Weights must be positive and real-valued but need not sum to 1.
|
|
* @param replace set true to sample with replacement
|
|
* @param engine random number engine
|
|
*
|
|
* @return xtensor containing 1D container of sampled elements
|
|
*/
|
|
template <class T, class W, class E>
|
|
xtensor<typename T::value_type, 1>
|
|
choice(const xexpression<T>& e, std::size_t n, const xexpression<W>& weights, bool replace, E& engine)
|
|
{
|
|
const auto& de = e.derived_cast();
|
|
const auto& dweights = weights.derived_cast();
|
|
XTENSOR_ASSERT((de.dimension() == 1));
|
|
XTENSOR_ASSERT((replace || n <= de.size()));
|
|
XTENSOR_ASSERT((de.size() == dweights.size()));
|
|
XTENSOR_ASSERT((de.dimension() == dweights.dimension()));
|
|
XTENSOR_ASSERT(xt::all(dweights >= 0));
|
|
static_assert(
|
|
std::is_floating_point<typename W::value_type>::value,
|
|
"Weight expression must be of floating point type"
|
|
);
|
|
using result_type = xtensor<typename T::value_type, 1>;
|
|
using size_type = typename result_type::size_type;
|
|
using weight_type = typename W::value_type;
|
|
result_type result;
|
|
result.resize({n});
|
|
|
|
if (replace)
|
|
{
|
|
// Sample u uniformly in the range [0, sum(weights)[
|
|
// The index idx of the sampled element is such that weight_cumul[idx - 1] <= u <
|
|
// weight_cumul[idx]. Where weight_cumul[-1] is implicitly 0, as the empty sum.
|
|
const auto wc = eval(cumsum(dweights));
|
|
std::uniform_real_distribution<weight_type> weight_dist{0, wc[wc.size() - 1]};
|
|
for (auto& x : result)
|
|
{
|
|
const auto u = weight_dist(engine);
|
|
const auto idx = static_cast<size_type>(
|
|
std::upper_bound(wc.cbegin(), wc.cend(), u) - wc.cbegin()
|
|
);
|
|
x = de[idx];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Compute (modified) keys as weight/randexp(1).
|
|
xtensor<weight_type, 1> keys;
|
|
keys.resize({dweights.size()});
|
|
std::exponential_distribution<weight_type> randexp{weight_type(1)};
|
|
std::transform(
|
|
dweights.cbegin(),
|
|
dweights.cend(),
|
|
keys.begin(),
|
|
[&randexp, &engine](auto w)
|
|
{
|
|
return w / randexp(engine);
|
|
}
|
|
);
|
|
|
|
// Find indexes for the n biggest key
|
|
xtensor<size_type, 1> indices = arange<size_type>(0, dweights.size());
|
|
std::partial_sort(
|
|
indices.begin(),
|
|
indices.begin() + n,
|
|
indices.end(),
|
|
[&keys](auto i, auto j)
|
|
{
|
|
return keys[i] > keys[j];
|
|
}
|
|
);
|
|
|
|
// Return samples with the n biggest keys
|
|
result = index_view(de, xtl::span<size_type>{indices.data(), n});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
#endif
|