diff --git a/DESCRIPTION b/DESCRIPTION index 61c11f7..f684e68 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,13 +1,13 @@ Package: dqrng Type: Package Title: Fast Pseudo Random Number Generators -Version: 0.3.1.3 +Version: 0.3.1.4 Authors@R: c( person("Ralf", "Stubner", email = "ralf.stubner@gmail.com", role = c("aut", "cre")), person("daqana GmbH", role = "cph"), - person("David Blackman", role = "ctb"), - person("Melissa O'Neill", email = "oneill@pcg-random.org", role = "ctb"), - person("Sebastiano Vigna", email = "vigna@acm.org", role = "ctb"), + person("David Blackman", role = "cph", comment = "Xoroshiro / Xoshiro family"), + person("Melissa O'Neill", email = "oneill@pcg-random.org", role = "cph", comment = "PCG family"), + person("Sebastiano Vigna", email = "vigna@acm.org", role = "cph", comment = "Xoroshiro / Xoshiro family"), person("Aaron", "Lun", role="ctb"), person("Kyle", "Butts", role = "ctb", email = "kyle.butts@colorado.edu"), person("Henrik", "Sloot", role = "ctb") @@ -23,10 +23,10 @@ Description: Several fast random number generators are provided as C++ The fast sampling methods support unweighted sampling both with and without replacement. These functions are exported to R and as a C++ interface and are enabled for use with the default 64 bit generator from the PCG family, - Xoroshiro128+ and Xoshiro256+ as well as the 64 bit version of the 20 rounds - Threefry engine (Salmon et al., 2011, ) as + Xoroshiro128+/++ and Xoshiro256+/++ as well as the 64 bit version of the 20 + rounds Threefry engine (Salmon et al., 2011, ) as provided by the package 'sitmo'. -License: AGPL-3 | file LICENSE +License: AGPL-3 Depends: R (>= 3.5.0) Imports: Rcpp (>= 0.12.16) LinkingTo: Rcpp, BH (>= 1.64.0-1), sitmo (>= 2.0.0) diff --git a/LICENSE b/LICENSE.note similarity index 95% rename from LICENSE rename to LICENSE.note index ea6e636..f449d15 100644 --- a/LICENSE +++ b/LICENSE.note @@ -6,6 +6,7 @@ URL: https://github.com/imneme/pcg-cpp File: inst/include/xoshiro.h Copyright: 2016, 2018 David Blackman and Sebastiano Vigna 2018 Ralf Stubner (daqana GmbH) + 2023 Ralf Stubner License: CC0 URL: http://xoshiro.di.unimi.it/. diff --git a/NAMESPACE b/NAMESPACE index 2788832..a462c3e 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -10,5 +10,7 @@ export(dqsample) export(dqsample.int) export(dqset.seed) export(generateSeedVectors) +export(register_methods) +export(restore_methods) importFrom(Rcpp,evalCpp) useDynLib(dqrng, .registration = TRUE) diff --git a/NEWS.md b/NEWS.md index 1638b45..2013334 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,13 +2,14 @@ ## 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 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 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)) ## Other changes * Decoupled the 'sitmo' package. It is now possible to use, e.g., the distribution functions from the header-only library without having an explicit `LinkingTo: sitmo`. * Make the internal RNG accessible from the outside (Henrik Sloot fixing [#41](https://github.com/daqana/dqrng/issues/41) in [#58](https://github.com/daqana/dqrng/pull/58)) * Add Xoroshiro128\*\*/++ and Xoshiro256\*\*/++ to `xoshiro.h` +* Allow uniform and normal distributions to be registered as user-supplied RNG within R. This happens automatically if the option `dqrng.register_methods` is set to `TRUE`. # dqrng 0.3.1 diff --git a/R/register_methods.R b/R/register_methods.R new file mode 100644 index 0000000..f879479 --- /dev/null +++ b/R/register_methods.R @@ -0,0 +1,79 @@ +state <- new.env(parent = emptyenv()) +state$RNGkind <- rep.int("default", 3) + +#' @title Registering as user-supplied RNG +#' +#' @description The random-number generators (RNG) from this package can be +#' registered as user-supplied RNG. This way all \code{r} functions make +#' use of the provided fast RNGs. +#' +#' @param kind Which methods should be registered? Either \code{"both"} or \code{"rng"}. +#' +#' @return Invisibly returns a three-element character vector of the RNG, normal +#' and sample kinds \emph{before} the call. +#' +#' @details Caveats: +#' \itemize{ +#' \item While \code{runif} and \code{dqrunif} as well as \code{rnorm} and +#' \code{dqrnorm} will produce the same results, this is not the case for +#' \code{rexp} and \code{dqrexp}. +#' \item The \code{dqr} functions are still faster than \code{r} +#' when many random numbers are generated. +#' \item You can use only the RNG from this package using +#' \code{register_method("rng")} or both the RNG and the Ziggurat method +#' for normal draws with \code{register_method("both")}. The latter +#' approach is used by default. Using only the Ziggurat method will give +#' \emph{undefined} behavior and is not supported! +#' \item Calling \code{dqset.seed(NULL)} re-initializes the RNG from R's RNG. +#' This no longer makes sense when the RNG has been registered as user-supplied +#' RNG. In that case \code{set.seed{NULL}} needs to be used. +#' \item With R's in-build RNGs one can get access to the internal state using +#' \code{.Random.seed}. This is not possible here, since the internal state +#' is a private member of the used C++ classes. +#' } +#' +#' You can automatically register these methods when loading this package by +#' setting the option \code{dqrng.register_methods} to \code{TRUE}, e.g. +#' with \code{options(dqrng.register_methods=TRUE)}. +#' +#' Notes on seeding: +#' \itemize{ +#' \item When a user-supplied RNG is registered, it is also seeded from the +#' previously used RNG. You will therefore get reproducible (but different) +#' whether you call \code{set.seed()} before or after \code{register_methods()}. +#' \item When called with a single integer as argument, both \code{set.seed()} +#' and \code{dqset.seed()} have the same effect. However, \code{dqset.seed()} +#' allows you to call it with two integers thereby supplying 64 bits of +#' initial state instead of just 32 bits. +#' } +#' @seealso \code{\link{RNGkind}} and \code{\link{Random.user}} +#' +#' @examples +#' register_methods() +#' # set.seed and dqset.seed influence both (dq)runif and (dq)rnorm +#' set.seed(4711); runif(5) +#' set.seed(4711); dqrunif(5) +#' dqset.seed(4711); rnorm(5) +#' dqset.seed(4711); dqrnorm(5) +#' # similarly for other r functions +#' set.seed(4711); rt(5, 10) +#' dqset.seed(4711); rt(5, 10) +#' # but (dq)rexp give different results +#' set.seed(4711); rexp(5, 10) +#' set.seed(4711); dqrexp(5, 10) +#' restore_methods() + +#' @rdname user-supplied-rng +#' @export +register_methods <- function(kind = c("both", "rng")) { + kind <- match.arg(kind) + switch(kind, + both = state$RNGkind <- RNGkind("user", "user"), + rng = state$RNGkind <- RNGkind("user")) +} + +#' @rdname user-supplied-rng +#' @export +restore_methods <- function() { + RNGkind(state$RNGkind[1], state$RNGkind[2], state$RNGkind[3]) +} diff --git a/R/zzz.R b/R/zzz.R index 44245dd..75a08ec 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -1,7 +1,16 @@ .onLoad <- function(libname, pkgname) { - if (!exists(".Random.seed", mode="numeric", envir=globalenv())) - set.seed(NULL) - original_seed <- get(".Random.seed", mode="numeric", envir=globalenv()) - on.exit(assign(".Random.seed", original_seed, envir=globalenv())) - dqset_seed(seed = NULL, stream = NULL) + if (getOption("dqrng.register_methods", FALSE)) + register_methods() + else { + if (!exists(".Random.seed", mode="numeric", envir=globalenv())) + set.seed(NULL) + original_seed <- get(".Random.seed", mode="numeric", envir=globalenv()) + on.exit(assign(".Random.seed", original_seed, envir=globalenv())) + dqset_seed(seed = NULL, stream = NULL) + } +} + +.onUnload <- function(libpath) { + if (getOption("dqrng.register_methods", FALSE)) + restore_methods() } diff --git a/README.Rmd b/README.Rmd index 83ce174..00b4fd1 100644 --- a/README.Rmd +++ b/README.Rmd @@ -101,6 +101,21 @@ u2 <- dqrunif(N) cor(u1, u2) ``` +It is also possible to register the supplied generators as user-supplied RNGs. This way `set.seed()` and `dqset.seed()` influence both `(dq)runif` and `(dq)rnorm` in the same way. This is also true for other `r` functions, but note that `rexp` and `dqrexp` still give different results: + +```{r register} +register_methods() +set.seed(4711); runif(5) +set.seed(4711); dqrunif(5) +dqset.seed(4711); rnorm(5) +dqset.seed(4711); dqrnorm(5) +set.seed(4711); rt(5, 10) +dqset.seed(4711); rt(5, 10) +set.seed(4711); rexp(5, 10) +set.seed(4711); dqrexp(5, 10) +restore_methods() +``` + ## Feedback All feedback (bug reports, security issues, feature requests, ...) should be provided as [issues](https://github.com/daqana/dqrng/issues). diff --git a/README.md b/README.md index 2c36efa..df189ef 100644 --- a/README.md +++ b/README.md @@ -47,9 +47,9 @@ build-in RNGs: library(dqrng) dqset.seed(42) dqrunif(5, min = 2, max = 10) -#> [1] 9.211802 2.616041 6.236331 4.588535 5.764814 +#> [1] 9.266963 4.644899 9.607483 3.635770 4.742639 dqrexp(5, rate = 4) -#> [1] 0.35118613 0.17656197 0.06844976 0.16984095 0.10096744 +#> [1] 0.111103883 0.084289794 0.003414377 0.042012033 0.143914583 ``` They are quite a bit faster, though: @@ -61,8 +61,8 @@ bm[, 1:4] #> # A tibble: 2 × 4 #> expression min median `itr/sec` #> -#> 1 rnorm(N) 612µs 685.2µs 1397. -#> 2 dqrnorm(N) 86µs 88.6µs 10388. +#> 1 rnorm(N) 607µs 660.2µs 1451. +#> 2 dqrnorm(N) 89.8µs 92.7µs 9896. ``` This is also true for the provided sampling functions with replacement: @@ -79,10 +79,10 @@ bm[, 1:4] #> # A tibble: 4 × 4 #> expression min median `itr/sec` #> -#> 1 sample.int(m, n, replace = TRUE) 6.88ms 7.63ms 114. -#> 2 sample.int(1000 * m, n, replace = TRUE) 8.72ms 9.55ms 96.1 -#> 3 dqsample.int(m, n, replace = TRUE) 482.21µs 810.29µs 1254. -#> 4 dqsample.int(1000 * m, n, replace = TRUE) 492.79µs 822.86µs 1275. +#> 1 sample.int(m, n, replace = TRUE) 6.88ms 7.08ms 139. +#> 2 sample.int(1000 * m, n, replace = TRUE) 8.72ms 8.93ms 110. +#> 3 dqsample.int(m, n, replace = TRUE) 410.9µs 434.24µs 2137. +#> 4 dqsample.int(1000 * m, n, replace = TRUE) 397.74µs 435.38µs 1930. ``` And without replacement: @@ -100,11 +100,11 @@ bm[, 1:4] #> # A tibble: 5 × 4 #> expression min median `itr/sec` #> -#> 1 sample.int(m, n) 40.1ms 42.54ms 23.5 -#> 2 sample.int(1000 * m, n) 12.19ms 14.38ms 67.8 -#> 3 sample.int(m, n, useHash = TRUE) 9.43ms 11.17ms 81.9 -#> 4 dqsample.int(m, n) 1.22ms 1.35ms 638. -#> 5 dqsample.int(1000 * m, n) 1.98ms 2.51ms 358. +#> 1 sample.int(m, n) 22.73ms 24.05ms 36.9 +#> 2 sample.int(1000 * m, n) 12.07ms 13.85ms 68.1 +#> 3 sample.int(m, n, useHash = TRUE) 9.57ms 12.63ms 74.4 +#> 4 dqsample.int(m, n) 1.11ms 1.2ms 696. +#> 5 dqsample.int(1000 * m, n) 1.95ms 2.69ms 293. ``` Note that sampling from `10^10` elements triggers “long-vector support” @@ -120,7 +120,34 @@ u1 <- dqrunif(N) dqset.seed(42, 2) u2 <- dqrunif(N) cor(u1, u2) -#> [1] -0.0005787967 +#> [1] 0.0009574617 +``` + +It is also possible to register the supplied generators as user-supplied +RNGs. This way `set.seed()` and `dqset.seed()` influence both +`(dq)runif` and `(dq)rnorm` in the same way. This is also true for other +`r` functions, but note that `rexp` and `dqrexp` still give +different results: + +``` r +register_methods() +set.seed(4711); runif(5) +#> [1] 0.3143534 0.7835753 0.1443660 0.1109871 0.6433407 +set.seed(4711); dqrunif(5) +#> [1] 0.3143534 0.7835753 0.1443660 0.1109871 0.6433407 +dqset.seed(4711); rnorm(5) +#> [1] -0.3618122 0.8199887 -0.4075635 0.2073972 -0.8038326 +dqset.seed(4711); dqrnorm(5) +#> [1] -0.3618122 0.8199887 -0.4075635 0.2073972 -0.8038326 +set.seed(4711); rt(5, 10) +#> [1] -0.3196113 -0.4095873 -1.2928241 0.2399470 -0.1068945 +dqset.seed(4711); rt(5, 10) +#> [1] -0.3196113 -0.4095873 -1.2928241 0.2399470 -0.1068945 +set.seed(4711); rexp(5, 10) +#> [1] 0.0950560698 0.0567150561 0.1541222748 0.2512966671 0.0002175758 +set.seed(4711); dqrexp(5, 10) +#> [1] 0.03254731 0.06855303 0.06977124 0.02579004 0.07629535 +restore_methods() ``` ## Feedback diff --git a/inst/include/dqrng_generator.h b/inst/include/dqrng_generator.h index a81974e..b42ff99 100644 --- a/inst/include/dqrng_generator.h +++ b/inst/include/dqrng_generator.h @@ -21,9 +21,9 @@ #include #include +#include #include #include -#include namespace dqrng { using default_64bit_generator = ::dqrng::xoroshiro128plusplus; @@ -185,6 +185,18 @@ inline void random_64bit_wrapper::seed(result_type seed, result_type stre } +template +typename std::enable_if::value, rng64_t>::type +generator () { + return std::make_shared>(); +} + +template +typename std::enable_if::value, rng64_t>::type +generator () { + return std::make_shared(); +} + template typename std::enable_if::value, rng64_t>::type generator (uint64_t seed) { diff --git a/man/dqrng-package.Rd b/man/dqrng-package.Rd index a7e9211..9487997 100644 --- a/man/dqrng-package.Rd +++ b/man/dqrng-package.Rd @@ -6,7 +6,7 @@ \alias{dqrng-package} \title{dqrng: Fast Pseudo Random Number Generators} \description{ -Several fast random number generators are provided as C++ header only libraries: The PCG family by O'Neill (2014 \url{https://www.cs.hmc.edu/tr/hmc-cs-2014-0905.pdf}) as well as the Xoroshiro / Xoshiro family by Blackman and Vigna (2021 \doi{10.1145/3460772}). In addition fast functions for generating random numbers according to a uniform, normal and exponential distribution are included. The latter two use the Ziggurat algorithm originally proposed by Marsaglia and Tsang (2000, \doi{10.18637/jss.v005.i08}). The fast sampling methods support unweighted sampling both with and without replacement. These functions are exported to R and as a C++ interface and are enabled for use with the default 64 bit generator from the PCG family, Xoroshiro128+ and Xoshiro256+ as well as the 64 bit version of the 20 rounds Threefry engine (Salmon et al., 2011, \doi{10.1145/2063384.2063405}) as provided by the package 'sitmo'. +Several fast random number generators are provided as C++ header only libraries: The PCG family by O'Neill (2014 \url{https://www.cs.hmc.edu/tr/hmc-cs-2014-0905.pdf}) as well as the Xoroshiro / Xoshiro family by Blackman and Vigna (2021 \doi{10.1145/3460772}). In addition fast functions for generating random numbers according to a uniform, normal and exponential distribution are included. The latter two use the Ziggurat algorithm originally proposed by Marsaglia and Tsang (2000, \doi{10.18637/jss.v005.i08}). The fast sampling methods support unweighted sampling both with and without replacement. These functions are exported to R and as a C++ interface and are enabled for use with the default 64 bit generator from the PCG family, Xoroshiro128+/++ and Xoshiro256+/++ as well as the 64 bit version of the 20 rounds Threefry engine (Salmon et al., 2011, \doi{10.1145/2063384.2063405}) as provided by the package 'sitmo'. } \seealso{ Useful links: @@ -23,9 +23,9 @@ Useful links: Other contributors: \itemize{ \item daqana GmbH [copyright holder] - \item David Blackman [contributor] - \item Melissa O'Neill \email{oneill@pcg-random.org} [contributor] - \item Sebastiano Vigna \email{vigna@acm.org} [contributor] + \item David Blackman (Xoroshiro / Xoshiro family) [copyright holder] + \item Melissa O'Neill \email{oneill@pcg-random.org} (PCG family) [copyright holder] + \item Sebastiano Vigna \email{vigna@acm.org} (Xoroshiro / Xoshiro family) [copyright holder] \item Aaron Lun [contributor] \item Kyle Butts \email{kyle.butts@colorado.edu} [contributor] \item Henrik Sloot [contributor] diff --git a/man/user-supplied-rng.Rd b/man/user-supplied-rng.Rd new file mode 100644 index 0000000..fb3b15d --- /dev/null +++ b/man/user-supplied-rng.Rd @@ -0,0 +1,77 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/register_methods.R +\name{register_methods} +\alias{register_methods} +\alias{restore_methods} +\title{Registering as user-supplied RNG} +\usage{ +register_methods(kind = c("both", "rng")) + +restore_methods() +} +\arguments{ +\item{kind}{Which methods should be registered? Either \code{"both"} or \code{"rng"}.} +} +\value{ +Invisibly returns a three-element character vector of the RNG, normal + and sample kinds \emph{before} the call. +} +\description{ +The random-number generators (RNG) from this package can be + registered as user-supplied RNG. This way all \code{r} functions make + use of the provided fast RNGs. +} +\details{ +Caveats: + \itemize{ + \item While \code{runif} and \code{dqrunif} as well as \code{rnorm} and + \code{dqrnorm} will produce the same results, this is not the case for + \code{rexp} and \code{dqrexp}. + \item The \code{dqr} functions are still faster than \code{r} + when many random numbers are generated. + \item You can use only the RNG from this package using + \code{register_method("rng")} or both the RNG and the Ziggurat method + for normal draws with \code{register_method("both")}. The latter + approach is used by default. Using only the Ziggurat method will give + \emph{undefined} behavior and is not supported! + \item Calling \code{dqset.seed(NULL)} re-initializes the RNG from R's RNG. + This no longer makes sense when the RNG has been registered as user-supplied + RNG. In that case \code{set.seed{NULL}} needs to be used. + \item With R's in-build RNGs one can get access to the internal state using + \code{.Random.seed}. This is not possible here, since the internal state + is a private member of the used C++ classes. + } + + You can automatically register these methods when loading this package by + setting the option \code{dqrng.register_methods} to \code{TRUE}, e.g. + with \code{options(dqrng.register_methods=TRUE)}. + + Notes on seeding: + \itemize{ + \item When a user-supplied RNG is registered, it is also seeded from the + previously used RNG. You will therefore get reproducible (but different) + whether you call \code{set.seed()} before or after \code{register_methods()}. + \item When called with a single integer as argument, both \code{set.seed()} + and \code{dqset.seed()} have the same effect. However, \code{dqset.seed()} + allows you to call it with two integers thereby supplying 64 bits of + initial state instead of just 32 bits. + } +} +\examples{ +register_methods() +# set.seed and dqset.seed influence both (dq)runif and (dq)rnorm +set.seed(4711); runif(5) +set.seed(4711); dqrunif(5) +dqset.seed(4711); rnorm(5) +dqset.seed(4711); dqrnorm(5) +# similarly for other r functions +set.seed(4711); rt(5, 10) +dqset.seed(4711); rt(5, 10) +# but (dq)rexp give different results +set.seed(4711); rexp(5, 10) +set.seed(4711); dqrexp(5, 10) +restore_methods() +} +\seealso{ +\code{\link{RNGkind}} and \code{\link{Random.user}} +} diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index bff9be7..9f302b5 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -493,7 +493,9 @@ static const R_CallMethodDef CallEntries[] = { {NULL, NULL, 0} }; +void dqrng_init(DllInfo *dll); RcppExport void R_init_dqrng(DllInfo *dll) { R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); R_useDynamicSymbols(dll, FALSE); + dqrng_init(dll); } diff --git a/src/dqrng.cpp b/src/dqrng.cpp index 886c0f8..a66a960 100644 --- a/src/dqrng.cpp +++ b/src/dqrng.cpp @@ -28,12 +28,13 @@ #include namespace { -dqrng::rng64_t init() { +dqrng::rng64_t rng = dqrng::generator(); + +void init() { Rcpp::RNGScope rngScope; Rcpp::IntegerVector seed(2, dqrng::R_random_int); - return dqrng::generator(dqrng::convert_seed(seed)); + rng->seed(dqrng::convert_seed(seed)); } -dqrng::rng64_t rng = nullptr; using generator = double(*)(); dqrng::uniform_distribution uniform{}; @@ -50,7 +51,7 @@ generator rexp_impl = [] () {return exponential(*rng);}; void dqset_seed(Rcpp::Nullable seed, Rcpp::Nullable stream = R_NilValue) { if (seed.isNull()) { - rng = init(); + init(); } else { uint64_t _seed = dqrng::convert_seed(seed.as()); if (stream.isNotNull()) { @@ -203,3 +204,43 @@ Rcpp::NumericVector dqsample_num(double m, return dqrng::sample::sample(rng, uint64_t(m), uint64_t(n), replace, offset); #endif } + +extern "C" { +// allow registering as user-supplied RNG +double * user_unif_rand(void) { + static double res; + res = dqrng::uniform01((*rng)()); + return &res; +} + +// https://stackoverflow.com/a/47839021/8416610 +Int32 unscramble(Int32 u) { + for (int j=0; j<50; j++) { + u = ((u - 1) * 2783094533); + } + return u; +} + +void user_unif_init(Int32 seed_in) { + rng->seed(uint64_t(unscramble(seed_in))); +} + +double * user_norm_rand(void) { + static double res; + using parm_t = decltype(normal)::param_type; + res = normal(*rng, parm_t(0.0, 1.0)); + return &res; +} +} // extern "C" + +static const R_CMethodDef cMethods[] = { + {"user_unif_rand", (DL_FUNC) &user_unif_rand, 0, NULL}, + {"user_unif_init", (DL_FUNC) &user_unif_init, 0, NULL}, + {"user_norm_rand", (DL_FUNC) &user_norm_rand, 0, NULL}, + {NULL, NULL, 0, NULL} +}; + +// [[Rcpp::init]] +void dqrng_init(DllInfo *dll) { + R_registerRoutines(dll, cMethods, NULL, NULL, NULL); +} diff --git a/tests/testthat/test-default.R b/tests/testthat/test-default.R index 319946b..6100fa6 100644 --- a/tests/testthat/test-default.R +++ b/tests/testthat/test-default.R @@ -17,6 +17,16 @@ test_that("setting seed produces identical uniformly distributed numbers", { expect_equal(u1, u2) }) +test_that("setting seed produces identical uniformly distributed numbers (user defined RNG)", { + register_methods() + dqset.seed(seed) + u1 <- dqrunif(10) + set.seed(seed) + u2 <- stats::runif(10) + expect_equal(u1, u2) + restore_methods() +}) + test_that("setting seed produces identical normaly distributed numbers", { dqset.seed(seed) n1 <- dqrnorm(10) @@ -25,6 +35,16 @@ test_that("setting seed produces identical normaly distributed numbers", { expect_equal(n1, n2) }) +test_that("setting seed produces identical normaly distributed numbers (user defined RNG)", { + register_methods() + dqset.seed(seed) + n1 <- dqrnorm(10) + set.seed(seed) + n2 <- stats::rnorm(10) + expect_equal(n1, n2) + restore_methods() +}) + test_that("setting seed produces identical exponenetially distributed numbers", { dqset.seed(seed) n1 <- dqrexp(10) diff --git a/vignettes/dqrng.Rmd b/vignettes/dqrng.Rmd index 5d98424..6ad7415 100644 --- a/vignettes/dqrng.Rmd +++ b/vignettes/dqrng.Rmd @@ -100,6 +100,22 @@ system.time(for (i in 1:100) sample.int(N, N/100)) system.time(for (i in 1:100) dqsample.int(N, N/100)) ``` +It is also possible to register the supplied generators as user-supplied RNGs. This way `set.seed()` and `dqset.seed()` influence both `(dq)runif` and `(dq)rnorm` in the same way. This is also true for other `r` functions, but note that `rexp` and `dqrexp` still give different results: + +```{r register} +register_methods() +set.seed(4711); stats::runif(5) +set.seed(4711); dqrng::dqrunif(5) +dqset.seed(4711); stats::rnorm(5) +dqset.seed(4711); dqrng::dqrnorm(5) +set.seed(4711); stats::rt(5, 10) +dqset.seed(4711); stats::rt(5, 10) +set.seed(4711); stats::rexp(5, 10) +set.seed(4711); dqrng::dqrexp(5, 10) +restore_methods() +``` + +You can automatically register these methods when loading this package by setting the option `dqrng.register_methods` to `TRUE`, e.g. with `options(dqrng.register_methods=TRUE)`. ## Usage from C++