Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restore pcg ctor #87

Merged
merged 6 commits into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.2.6
Version: 0.3.2.7
Authors@R: c(
person("Ralf", "Stubner", email = "[email protected]", role = c("aut", "cre"), comment = c(ORCID = "0009-0009-1908-106X")),
person("daqana GmbH", role = "cph"),
Expand Down
3 changes: 1 addition & 2 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

* 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))
* The two argument constructor and `seed` function from PCG has [surprising properties](https://github.com/imneme/pcg-cpp/issues/91): it is not identical to the one argument version followed by `set_stream(stream)`. For consistency with the new `clone(stream)` method, the two argument versions are no longer used. This influences code that uses multiple streams with PCG together with the tooling from this package, e.g. the example code in the vignette on parallel RNG usage. In addition, setting the stream on PCG64 via `dqset.seed(seed, stream)` or at the C++ level using the interface provided by dqrng will be relative to the current stream, i.e. setting `stream=0` will not change the RNG. This is for consistency with the other provided RNGs. You still get the standard behaviour if you are using the C++ classes for PCG directly.

## Other changes

Expand All @@ -15,7 +14,7 @@
* Add missing inline attributes and limit the included Rcpp headers in `dqrng_types.h` ([#75](https://github.com/daqana/dqrng/pull/75) together with Paul Liétar)
* Add I/O methods for the RNG's internal state (fixing [#66](https://github.com/daqana/dqrng/issues/66) in [#78](https://github.com/daqana/dqrng/pull/78))
* Extend `random_64bit_generator` with additional convenience methods (fixing [#64](https://github.com/daqana/dqrng/issues/64) in [#79](https://github.com/daqana/dqrng/pull/79))
* A `clone(stream)` method to allow using the global RNG state for parallel computation
* A `clone(stream)` method to allow using the global RNG state for parallel computation. Note that for consistency with the other provided RNGs, `stream` is counted relative to the current stream for PCG64.
* New methods `variate<dist>(param)`, `generate<dist>(container, param)` etc. using and inspired by [`randutils`](https://www.pcg-random.org/posts/ease-of-use-without-loss-of-power.html).
* The scalar functions `dqrng::runif`, `dqrng::rnorm` and `dqrng::rexp` available from `dqrng.h` have been deprecated and will be removed in a future release. Please use the more flexible and faster `dqrng::random_64bit_accessor` together with `variate<Dist>()` instead. The same applies to `dqrng::uniform01` from `dqrng_distribution.h`, which can be replaced by the member function `dqrng::random_64bit_generator::uniform01`.
* New template function `dqrng::extra::parallel_generate` in `dqrng_extra/parallel_generate.h` as an example for using the global RNG in a parallel context (fixing [#77](https://github.com/daqana/dqrng/issues/77) in [#82](https://github.com/daqana/dqrng/issues/82) together with Philippe Grosjean)
Expand Down
9 changes: 8 additions & 1 deletion inst/include/dqrng_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class random_64bit_wrapper : public random_64bit_generator {
random_64bit_wrapper() : gen() {};
random_64bit_wrapper(RNG _gen) : gen(_gen) {};
random_64bit_wrapper(result_type seed) : gen(seed) {};
random_64bit_wrapper(result_type seed, result_type stream) : gen(seed) {this->set_stream(stream);};
random_64bit_wrapper(result_type seed, result_type stream) : gen() {this->seed(seed, stream);};
virtual result_type operator() () override {return gen();}
virtual void seed(result_type seed) override {cache = false; gen.seed(seed);}
virtual void seed(result_type seed, result_type stream) override {cache = false; gen.seed(seed); this->set_stream(stream);}
Expand Down Expand Up @@ -115,6 +115,13 @@ inline void random_64bit_wrapper<pcg64>::set_stream(result_type stream) {
gen.set_stream(state[1]/2 + stream);
}

// keep using the two argument ctor for PCG for backwards compatibility
template<>
inline void random_64bit_wrapper<pcg64>::seed(result_type seed, result_type stream) {
gen.seed(seed, stream);
cache = false;
}

inline uint64_t get_seed_from_r() {
Rcpp::RNGScope rngScope;
Rcpp::IntegerVector seed(2, dqrng::R_random_int);
Expand Down
4 changes: 2 additions & 2 deletions vignettes/cpp-api.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ void dqrng::dqrng_set_state(std::vector<std::string> state)
Rcpp::XPtr<dqrng::random_64bit_generator> dqrng::get_rng()
```

Direct usage of this method is **discouraged**. The prefered way of accesing the global RNG is to instantiate `dqrng::random_64bit_accessor` within your function. Note that you MUST NOT delete the global RNG. Using `dqrng::random_64bit_accessor` makes this impossible. In addition, you SHOULD NOT store a reference to the RNG permanently, because it can be invalidated by calls to `dqRNGkind`. Therefore, instances of `dqrng::random_64bit_accessor` SHOULD be stored as (non-static) variables in functions.
Direct usage of this method is **discouraged**. The preferred way of accessing the global RNG is to instantiate `dqrng::random_64bit_accessor` within your function. Note that you MUST NOT delete the global RNG. Using `dqrng::random_64bit_accessor` makes this impossible. In addition, you SHOULD NOT store a reference to the RNG permanently, because it can be invalidated by calls to `dqRNGkind`. Therefore, instances of `dqrng::random_64bit_accessor` SHOULD be stored as (non-static) variables in functions.

Note that `dqrng::random_64bit_accessor` supports [UniformRandomBitGenerator](https://en.cppreference.com/w/cpp/named_req/UniformRandomBitGenerator) and can therefore be used together with any C++11 distribtion function. In addition, the following functions are supported, since they are inherited from the abstract parent class `random_64bit_generator`:
Note that `dqrng::random_64bit_accessor` supports [UniformRandomBitGenerator](https://en.cppreference.com/w/cpp/named_req/UniformRandomBitGenerator) and can therefore be used together with any C++11 distribution function. In addition, the following functions are supported, since they are inherited from the abstract parent class `random_64bit_generator`:

```cpp
// clone RNG and select a different stream
Expand Down
24 changes: 11 additions & 13 deletions vignettes/parallel.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,6 @@ From the PCG family we will look at pcg64, a 64-bit generator with a period of $
It offers the function [`advance(int n)`](https://www.pcg-random.org/using-pcg-cpp.html#void-advance-state-type-delta), which is equivalent to `n` random draws but scales as $O(ln(n))$ instead of $O(n)$.
In addition, it offers $2^{127}$ separate streams that can be enabled via the function [`set_stream(int n)`](https://www.pcg-random.org/using-pcg-cpp.html#void-pcg32-set-stream-state-type-stream) or the [two argument constructor](https://www.pcg-random.org/using-pcg-cpp.html#pcg32-pcg32-constructor) with `seed` and `stream`.
When used from R or C++ with the two argument `dqset.seed` and `dqset_seed` you get $2^{64}$ streams out of the possible $2^{127}$ separate streams.
Note that **here** in contrast to the default PCG implementation, streams are counted from the default stream, i.e. setting `stream` to `0` or not at all will give the same RNG.
This brings PCG in line with the other RNGs.

In the following example a matrix with random numbers is generated in parallel using RcppParallel.
Instead of using the more traditional approach of generating the random numbers from a certain distribution, we are using the fast sampling methods from `dqrng_sample.h`.
Expand Down Expand Up @@ -206,23 +204,23 @@ symnum(x = cor(res), cutpoints = c(0.001, 0.003, 0.999),
Head of the random matrix:

[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
[1,] 16583 94445 14240 53296 15021 72402 12677 47777
[2,] 49983 29144 49983 80361 49983 60477 49983 6255
[3,] 56598 19559 87036 76208 8804 67365 60990 22777
[4,] 64117 18406 69814 56954 14447 27027 50950 10545
[5,] 48709 62087 66064 2944 11887 75422 5823 48069
[6,] 81061 71093 52958 6324 82242 68439 68331 41016
[1,] 67984 16279 69262 7126 21441 37720 51107 51045
[2,] 69310 21713 82885 81157 54051 5261 91165 17833
[3,] 76742 31232 78953 4626 94939 29416 85652 78296
[4,] 76349 47427 1770 37957 33888 59134 94591 65793
[5,] 85008 89224 43493 7925 60866 2464 14080 10763
[6,] 38017 88509 51195 73086 1883 68193 75259 62216

Correlation matrix:

[1,] 1
[2,] 1
[3,] ? 1
[4,] ? 1
[4,] ? 1
[5,] 1
[6,] ? 1
[7,] ? ? ? ? ? 1
[8,] ? 1
[6,] ? ? ? 1
[7,] ? 1
[8,] ? 1
attr(,"legend")
[1] 0 ‘ ’ 0.001 ‘?’ 0.003 ‘!’ 0.999 ‘1’ 1

Expand Down Expand Up @@ -307,7 +305,7 @@ This way, the result becomes independent of the used amount of parallelism.
However, one has to consider one important aspect:
After the parallel for loop, the global RNG has not advanced at all.
Calling the function repeatedly would lead to identical results.
To circumvent this, one of the stream specific RNGs is an exact clone of the global RNG (`clone(stream=0)`) and the state of this RNG after processing its stream is saved and used to overwrite the global RNG's state.
To circumvent this, one of the stream specific RNGs is an exact clone of the global RNG (`clone(stream=0)`) and the state of this RNG after processing its stream is saved and used to overwrite the global RNG's state.^[Note that **here** in contrast to the default PCG implementation, streams are counted from the current stream, i.e. setting `stream` to `0` will give the same RNG. This brings PCG in line with the other RNGs.]
This way, the global RNG's state advances as if it had processed one of the streams and successive calls to `parallel_generate()` produce different random numbers as expected.

```{r, eval=evaluate}
Expand Down
Loading