Skip to content

Commit

Permalink
Change internal storage type to Rcpp::XPtr<random_64bit_generator> (#70)
Browse files Browse the repository at this point in the history
This simplifies exchange of the RNG via R's C API.

Don't use Rcpp:Vector<> classes internally. Instead, the class to be used needs to be specified explicitly. This allows usage of std::vector etc., that do not use R's memory allocation. This makes sampling templates multi-thread compatible,
  • Loading branch information
rstub authored Oct 7, 2023
1 parent 3dc8bf2 commit cd8bfa8
Show file tree
Hide file tree
Showing 14 changed files with 102 additions and 97 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: dqrng
Type: Package
Title: Fast Pseudo Random Number Generators
Version: 0.3.1.4
Version: 0.3.1.5
Authors@R: c(
person("Ralf", "Stubner", email = "[email protected]", role = c("aut", "cre")),
person("daqana GmbH", role = "cph"),
Expand Down
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Breaking changes

* The default RNG has changed from Xoroshiro128+ to Xoroshiro128++. The older generators Xoroshiro128+ and Xoshiro256+ are still available but should only be used for backward compatibility or for generating floating point numbers, i.e. not sampling etc. ([#57](https://github.com/daqana/dqrng/pull/57) fixing [#56](https://github.com/daqana/dqrng/issues/56))
* The `dqrng::rng64_t` type has been changed to use `Rcpp::XPtr` instead of `std::shared_ptr` and the functions from `dqrng_sample.h` now expect a reference to `dqrng::random_64bit_generator` instead of `dqrng::rng64_t` ([#70](https://github.com/daqana/dqrng/pull/70) fixing [#63](https://github.com/daqana/dqrng/issues/63))

## Other changes

Expand Down
8 changes: 4 additions & 4 deletions R/RcppExports.R
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ dqrrademacher <- function(n) {
.Call(`_dqrng_dqrrademacher`, n)
}

dqsample_int <- function(m, n, replace = FALSE, probs = NULL, offset = 0L) {
.Call(`_dqrng_dqsample_int`, m, n, replace, probs, offset)
dqsample_int <- function(n, size, replace = FALSE, probs = NULL, offset = 0L) {
.Call(`_dqrng_dqsample_int`, n, size, replace, probs, offset)
}

dqsample_num <- function(m, n, replace = FALSE, probs = NULL, offset = 0L) {
.Call(`_dqrng_dqsample_num`, m, n, replace, probs, offset)
dqsample_num <- function(n, size, replace = FALSE, probs = NULL, offset = 0L) {
.Call(`_dqrng_dqsample_num`, n, size, replace, probs, offset)
}

#' Generate seed as a integer vector
Expand Down
2 changes: 1 addition & 1 deletion inst/include/dqrng.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@
#include "dqrng_RcppExports.h"

namespace dqrng {
random_64bit_accessor::random_64bit_accessor() : gen(dqrng::get_rng().get()) {}
random_64bit_accessor::random_64bit_accessor() : gen(dqrng::get_rng()) {}
} // namespace dqrng
#endif // dqrng_H
14 changes: 7 additions & 7 deletions inst/include/dqrng_RcppExports.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,11 @@ namespace dqrng {
return Rcpp::as<double >(rcpp_result_gen);
}

inline Rcpp::XPtr<dqrng::rng64_t::element_type> get_rng() {
inline Rcpp::XPtr<dqrng::random_64bit_generator> get_rng() {
typedef SEXP(*Ptr_get_rng)();
static Ptr_get_rng p_get_rng = NULL;
if (p_get_rng == NULL) {
validateSignature("Rcpp::XPtr<dqrng::rng64_t::element_type>(*get_rng)()");
validateSignature("Rcpp::XPtr<dqrng::random_64bit_generator>(*get_rng)()");
p_get_rng = (Ptr_get_rng)R_GetCCallable("dqrng", "_dqrng_get_rng");
}
RObject rcpp_result_gen;
Expand All @@ -200,7 +200,7 @@ namespace dqrng {
throw Rcpp::LongjumpException(rcpp_result_gen);
if (rcpp_result_gen.inherits("try-error"))
throw Rcpp::exception(Rcpp::as<std::string>(rcpp_result_gen).c_str());
return Rcpp::as<Rcpp::XPtr<dqrng::rng64_t::element_type> >(rcpp_result_gen);
return Rcpp::as<Rcpp::XPtr<dqrng::random_64bit_generator> >(rcpp_result_gen);
}

inline Rcpp::IntegerVector dqrrademacher(size_t n) {
Expand All @@ -223,7 +223,7 @@ namespace dqrng {
return Rcpp::as<Rcpp::IntegerVector >(rcpp_result_gen);
}

inline Rcpp::IntegerVector dqsample_int(int m, int n, bool replace = false, Rcpp::Nullable<Rcpp::NumericVector> probs = R_NilValue, int offset = 0) {
inline Rcpp::IntegerVector dqsample_int(int n, int size, bool replace = false, Rcpp::Nullable<Rcpp::NumericVector> probs = R_NilValue, int offset = 0) {
typedef SEXP(*Ptr_dqsample_int)(SEXP,SEXP,SEXP,SEXP,SEXP);
static Ptr_dqsample_int p_dqsample_int = NULL;
if (p_dqsample_int == NULL) {
Expand All @@ -232,7 +232,7 @@ namespace dqrng {
}
RObject rcpp_result_gen;
{
rcpp_result_gen = p_dqsample_int(Shield<SEXP>(Rcpp::wrap(m)), Shield<SEXP>(Rcpp::wrap(n)), Shield<SEXP>(Rcpp::wrap(replace)), Shield<SEXP>(Rcpp::wrap(probs)), Shield<SEXP>(Rcpp::wrap(offset)));
rcpp_result_gen = p_dqsample_int(Shield<SEXP>(Rcpp::wrap(n)), Shield<SEXP>(Rcpp::wrap(size)), Shield<SEXP>(Rcpp::wrap(replace)), Shield<SEXP>(Rcpp::wrap(probs)), Shield<SEXP>(Rcpp::wrap(offset)));
}
if (rcpp_result_gen.inherits("interrupted-error"))
throw Rcpp::internal::InterruptedException();
Expand All @@ -243,7 +243,7 @@ namespace dqrng {
return Rcpp::as<Rcpp::IntegerVector >(rcpp_result_gen);
}

inline Rcpp::NumericVector dqsample_num(double m, double n, bool replace = false, Rcpp::Nullable<Rcpp::NumericVector> probs = R_NilValue, int offset = 0) {
inline Rcpp::NumericVector dqsample_num(double n, double size, bool replace = false, Rcpp::Nullable<Rcpp::NumericVector> probs = R_NilValue, int offset = 0) {
typedef SEXP(*Ptr_dqsample_num)(SEXP,SEXP,SEXP,SEXP,SEXP);
static Ptr_dqsample_num p_dqsample_num = NULL;
if (p_dqsample_num == NULL) {
Expand All @@ -252,7 +252,7 @@ namespace dqrng {
}
RObject rcpp_result_gen;
{
rcpp_result_gen = p_dqsample_num(Shield<SEXP>(Rcpp::wrap(m)), Shield<SEXP>(Rcpp::wrap(n)), Shield<SEXP>(Rcpp::wrap(replace)), Shield<SEXP>(Rcpp::wrap(probs)), Shield<SEXP>(Rcpp::wrap(offset)));
rcpp_result_gen = p_dqsample_num(Shield<SEXP>(Rcpp::wrap(n)), Shield<SEXP>(Rcpp::wrap(size)), Shield<SEXP>(Rcpp::wrap(replace)), Shield<SEXP>(Rcpp::wrap(probs)), Shield<SEXP>(Rcpp::wrap(offset)));
}
if (rcpp_result_gen.inherits("interrupted-error"))
throw Rcpp::internal::InterruptedException();
Expand Down
15 changes: 9 additions & 6 deletions inst/include/dqrng_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@
#include <dqrng_types.h>
#include <xoshiro.h>
#include <pcg_random.hpp>
#include <Rcpp.h>


namespace dqrng {
using rng64_t = Rcpp::XPtr<random_64bit_generator>;
using default_64bit_generator = ::dqrng::xoroshiro128plusplus;

template<typename RNG>
Expand Down Expand Up @@ -188,37 +191,37 @@ inline void random_64bit_wrapper<pcg64>::seed(result_type seed, result_type stre
template<typename RNG = default_64bit_generator>
typename std::enable_if<!std::is_base_of<random_64bit_generator, RNG>::value, rng64_t>::type
generator () {
return std::make_shared<random_64bit_wrapper<RNG>>();
return rng64_t(new random_64bit_wrapper<RNG>());
}

template<typename RNG = default_64bit_generator>
typename std::enable_if<std::is_base_of<random_64bit_generator, RNG>::value, rng64_t>::type
generator () {
return std::make_shared<RNG>();
return rng64_t(new RNG());
}

template<typename RNG = default_64bit_generator>
typename std::enable_if<!std::is_base_of<random_64bit_generator, RNG>::value, rng64_t>::type
generator (uint64_t seed) {
return std::make_shared<random_64bit_wrapper<RNG>>(seed);
return rng64_t(new random_64bit_wrapper<RNG>(seed));
}

template<typename RNG = default_64bit_generator>
typename std::enable_if<std::is_base_of<random_64bit_generator, RNG>::value, rng64_t>::type
generator (uint64_t seed) {
return std::make_shared<RNG>(seed);
return rng64_t(new RNG(seed));
}

template<typename RNG = default_64bit_generator>
typename std::enable_if<!std::is_base_of<random_64bit_generator, RNG>::value, rng64_t>::type
generator (uint64_t seed, uint64_t stream) {
return std::make_shared<random_64bit_wrapper<RNG>>(seed, stream);
return rng64_t(new random_64bit_wrapper<RNG>(seed, stream));
}

template<typename RNG = default_64bit_generator>
typename std::enable_if<std::is_base_of<random_64bit_generator, RNG>::value, rng64_t>::type
generator (uint64_t seed, uint64_t stream) {
return std::make_shared<RNG>(seed, stream);
return rng64_t(new RNG(seed, stream));
}
} // namespace dqrng

Expand Down
73 changes: 35 additions & 38 deletions inst/include/dqrng_sample.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2018-2019 Ralf Stubner (daqana GmbH)
// Copyright 2022 Ralf Stubner
// Copyright 2022-2023 Ralf Stubner
//
// This file is part of dqrng.
//
Expand All @@ -21,62 +21,59 @@

#include <mystdint.h>
#include <Rcpp.h>
#include <dqrng_generator.h>
#include <dqrng_types.h>
#include <minimal_int_set.h>

namespace dqrng {
namespace sample {
template<int RTYPE, typename INT>
inline Rcpp::Vector<RTYPE> replacement(dqrng::rng64_t &rng, INT m, INT n, int offset) {
using storage_t = typename Rcpp::traits::storage_type<RTYPE>::type;
Rcpp::Vector<RTYPE> result(Rcpp::no_init(n));
template<typename VEC, typename INT>
inline VEC replacement(dqrng::random_64bit_generator &rng, INT n, INT size, int offset) {
VEC result(size);
std::generate(result.begin(), result.end(),
[m, offset, rng] () {return static_cast<storage_t>(offset + (*rng)(m));});
[n, offset, &rng] () {return (offset + rng(n));});
return result;
}

template<int RTYPE, typename INT>
inline Rcpp::Vector<RTYPE> no_replacement_shuffle(dqrng::rng64_t &rng, INT m, INT n, int offset) {
using storage_t = typename Rcpp::traits::storage_type<RTYPE>::type;
Rcpp::Vector<RTYPE> tmp(Rcpp::no_init(m));
std::iota(tmp.begin(), tmp.end(), static_cast<storage_t>(offset));
for (INT i = 0; i < n; ++i) {
std::swap(tmp[i], tmp[i + (*rng)(m - i)]);
template<typename VEC, typename INT>
inline VEC no_replacement_shuffle(dqrng::random_64bit_generator &rng, INT n, INT size, int offset) {
VEC tmp(n);
std::iota(tmp.begin(), tmp.end(), (offset));
for (INT i = 0; i < size; ++i) {
std::swap(tmp[i], tmp[i + rng(n - i)]);
}
if (m == n)
if (n == size)
return tmp;
else
return Rcpp::Vector<RTYPE>(tmp.begin(), tmp.begin() + n);
return VEC(tmp.begin(), tmp.begin() + size);
}

template<int RTYPE, typename INT, typename SET>
inline Rcpp::Vector<RTYPE> no_replacement_set(dqrng::rng64_t &rng, INT m, INT n, int offset) {
using storage_t = typename Rcpp::traits::storage_type<RTYPE>::type;
Rcpp::Vector<RTYPE> result(Rcpp::no_init(n));
SET elems(m, n);
for (INT i = 0; i < n; ++i) {
INT v = (*rng)(m);
while (!elems.insert(v)) {
v = (*rng)(m);
}
result(i) = static_cast<storage_t>(offset + v);
template<typename VEC, typename INT, typename SET>
inline VEC no_replacement_set(dqrng::random_64bit_generator &rng, INT n, INT size, int offset) {
VEC result(size);
SET elems(n, size);
for (INT i = 0; i < size; ++i) {
INT v;
do {
v = rng(n);
} while (!elems.insert(v));
result[i] = (offset + v);
}
return result;
}

template<int RTYPE, typename INT>
inline Rcpp::Vector<RTYPE> sample(dqrng::rng64_t &rng, INT m, INT n, bool replace, int offset = 0) {
if (replace || n <= 1) {
return dqrng::sample::replacement<RTYPE, INT>(rng, m, n, offset);
template<typename VEC, typename INT>
inline VEC sample(dqrng::random_64bit_generator &rng, INT n, INT size, bool replace, int offset = 0) {
if (replace || size <= 1) {
return dqrng::sample::replacement<VEC, INT>(rng, n, size, offset);
} else {
if (!(m >= n))
Rcpp::stop("Argument requirements not fulfilled: m >= n");
if (m < 2 * n) {
return dqrng::sample::no_replacement_shuffle<RTYPE, INT>(rng, m, n, offset);
} else if (m < 1000 * n) {
return dqrng::sample::no_replacement_set<RTYPE, INT, dqrng::minimal_bit_set>(rng, m, n, offset);
if (!(n >= size))
Rcpp::stop("Argument requirements not fulfilled: n >= size");
if (n < 2 * size) {
return dqrng::sample::no_replacement_shuffle<VEC, INT>(rng, n, size, offset);
} else if (n < 1000 * size) {
return dqrng::sample::no_replacement_set<VEC, INT, dqrng::minimal_bit_set>(rng, n, size, offset);
} else {
return dqrng::sample::no_replacement_set<RTYPE, INT, dqrng::minimal_hash_set<INT>>(rng, m, n, offset);
return dqrng::sample::no_replacement_set<VEC, INT, dqrng::minimal_hash_set<INT>>(rng, n, size, offset);
}
}
}
Expand Down
6 changes: 2 additions & 4 deletions inst/include/dqrng_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
#define DQRNG_TYPES_H 1

#include <mystdint.h>
#include <memory>
#include <stdexcept>
#include <Rcpp/Lightest>

namespace dqrng {

Expand Down Expand Up @@ -53,11 +53,9 @@ class random_64bit_generator {
}
};

using rng64_t = std::shared_ptr<random_64bit_generator>;

class random_64bit_accessor : public random_64bit_generator {
private:
dqrng::random_64bit_generator *gen;
Rcpp::XPtr<dqrng::random_64bit_generator> gen;

protected:
virtual void output(std::ostream& ost) const override {
Expand Down
2 changes: 1 addition & 1 deletion inst/include/minimal_int_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
// Common interface:
// * ctor (m, n)
// * bool insert(entry, check = true)
// returns true if insert succesfull and check == true
// returns true if insert successful and check == true

namespace dqrng {
template<typename T = uint32_t>
Expand Down
28 changes: 14 additions & 14 deletions src/RcppExports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ RcppExport SEXP _dqrng_rexp(SEXP rateSEXP) {
return rcpp_result_gen;
}
// get_rng
Rcpp::XPtr<dqrng::rng64_t::element_type> get_rng();
Rcpp::XPtr<dqrng::random_64bit_generator> get_rng();
static SEXP _dqrng_get_rng_try() {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Expand Down Expand Up @@ -351,23 +351,23 @@ RcppExport SEXP _dqrng_dqrrademacher(SEXP nSEXP) {
return rcpp_result_gen;
}
// dqsample_int
Rcpp::IntegerVector dqsample_int(int m, int n, bool replace, Rcpp::Nullable<Rcpp::NumericVector> probs, int offset);
static SEXP _dqrng_dqsample_int_try(SEXP mSEXP, SEXP nSEXP, SEXP replaceSEXP, SEXP probsSEXP, SEXP offsetSEXP) {
Rcpp::IntegerVector dqsample_int(int n, int size, bool replace, Rcpp::Nullable<Rcpp::NumericVector> probs, int offset);
static SEXP _dqrng_dqsample_int_try(SEXP nSEXP, SEXP sizeSEXP, SEXP replaceSEXP, SEXP probsSEXP, SEXP offsetSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::traits::input_parameter< int >::type m(mSEXP);
Rcpp::traits::input_parameter< int >::type n(nSEXP);
Rcpp::traits::input_parameter< int >::type size(sizeSEXP);
Rcpp::traits::input_parameter< bool >::type replace(replaceSEXP);
Rcpp::traits::input_parameter< Rcpp::Nullable<Rcpp::NumericVector> >::type probs(probsSEXP);
Rcpp::traits::input_parameter< int >::type offset(offsetSEXP);
rcpp_result_gen = Rcpp::wrap(dqsample_int(m, n, replace, probs, offset));
rcpp_result_gen = Rcpp::wrap(dqsample_int(n, size, replace, probs, offset));
return rcpp_result_gen;
END_RCPP_RETURN_ERROR
}
RcppExport SEXP _dqrng_dqsample_int(SEXP mSEXP, SEXP nSEXP, SEXP replaceSEXP, SEXP probsSEXP, SEXP offsetSEXP) {
RcppExport SEXP _dqrng_dqsample_int(SEXP nSEXP, SEXP sizeSEXP, SEXP replaceSEXP, SEXP probsSEXP, SEXP offsetSEXP) {
SEXP rcpp_result_gen;
{
rcpp_result_gen = PROTECT(_dqrng_dqsample_int_try(mSEXP, nSEXP, replaceSEXP, probsSEXP, offsetSEXP));
rcpp_result_gen = PROTECT(_dqrng_dqsample_int_try(nSEXP, sizeSEXP, replaceSEXP, probsSEXP, offsetSEXP));
}
Rboolean rcpp_isInterrupt_gen = Rf_inherits(rcpp_result_gen, "interrupted-error");
if (rcpp_isInterrupt_gen) {
Expand All @@ -388,23 +388,23 @@ RcppExport SEXP _dqrng_dqsample_int(SEXP mSEXP, SEXP nSEXP, SEXP replaceSEXP, SE
return rcpp_result_gen;
}
// dqsample_num
Rcpp::NumericVector dqsample_num(double m, double n, bool replace, Rcpp::Nullable<Rcpp::NumericVector> probs, int offset);
static SEXP _dqrng_dqsample_num_try(SEXP mSEXP, SEXP nSEXP, SEXP replaceSEXP, SEXP probsSEXP, SEXP offsetSEXP) {
Rcpp::NumericVector dqsample_num(double n, double size, bool replace, Rcpp::Nullable<Rcpp::NumericVector> probs, int offset);
static SEXP _dqrng_dqsample_num_try(SEXP nSEXP, SEXP sizeSEXP, SEXP replaceSEXP, SEXP probsSEXP, SEXP offsetSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::traits::input_parameter< double >::type m(mSEXP);
Rcpp::traits::input_parameter< double >::type n(nSEXP);
Rcpp::traits::input_parameter< double >::type size(sizeSEXP);
Rcpp::traits::input_parameter< bool >::type replace(replaceSEXP);
Rcpp::traits::input_parameter< Rcpp::Nullable<Rcpp::NumericVector> >::type probs(probsSEXP);
Rcpp::traits::input_parameter< int >::type offset(offsetSEXP);
rcpp_result_gen = Rcpp::wrap(dqsample_num(m, n, replace, probs, offset));
rcpp_result_gen = Rcpp::wrap(dqsample_num(n, size, replace, probs, offset));
return rcpp_result_gen;
END_RCPP_RETURN_ERROR
}
RcppExport SEXP _dqrng_dqsample_num(SEXP mSEXP, SEXP nSEXP, SEXP replaceSEXP, SEXP probsSEXP, SEXP offsetSEXP) {
RcppExport SEXP _dqrng_dqsample_num(SEXP nSEXP, SEXP sizeSEXP, SEXP replaceSEXP, SEXP probsSEXP, SEXP offsetSEXP) {
SEXP rcpp_result_gen;
{
rcpp_result_gen = PROTECT(_dqrng_dqsample_num_try(mSEXP, nSEXP, replaceSEXP, probsSEXP, offsetSEXP));
rcpp_result_gen = PROTECT(_dqrng_dqsample_num_try(nSEXP, sizeSEXP, replaceSEXP, probsSEXP, offsetSEXP));
}
Rboolean rcpp_isInterrupt_gen = Rf_inherits(rcpp_result_gen, "interrupted-error");
if (rcpp_isInterrupt_gen) {
Expand Down Expand Up @@ -449,7 +449,7 @@ static int _dqrng_RcppExport_validate(const char* sig) {
signatures.insert("double(*rnorm)(double,double)");
signatures.insert("Rcpp::NumericVector(*dqrexp)(size_t,double)");
signatures.insert("double(*rexp)(double)");
signatures.insert("Rcpp::XPtr<dqrng::rng64_t::element_type>(*get_rng)()");
signatures.insert("Rcpp::XPtr<dqrng::random_64bit_generator>(*get_rng)()");
signatures.insert("Rcpp::IntegerVector(*dqrrademacher)(size_t)");
signatures.insert("Rcpp::IntegerVector(*dqsample_int)(int,int,bool,Rcpp::Nullable<Rcpp::NumericVector>,int)");
signatures.insert("Rcpp::NumericVector(*dqsample_num)(double,double,bool,Rcpp::Nullable<Rcpp::NumericVector>,int)");
Expand Down
Loading

0 comments on commit cd8bfa8

Please sign in to comment.