imneme / pcg-cpp Goto Github PK
View Code? Open in Web Editor NEWPCG — C++ Implementation
License: Apache License 2.0
PCG — C++ Implementation
License: Apache License 2.0
Minimal example:
#include <pcg_random.hpp>
int main() { return 0; }
Compiling twice gives a cache miss the second time:
$ ccache g++ -c -Ipcg-cpp-0.98.1/include pcg-example.cpp
$ ccache -s
cache directory /home/vedranmiletic/.ccache
primary config /home/vedranmiletic/.ccache/ccache.conf
secondary config (readonly) /etc/ccache.conf
stats updated Thu Feb 6 11:39:36 2020
cache hit (direct) 0
cache hit (preprocessed) 0
cache miss 1
cache hit rate 0.00 %
cleanups performed 0
files in cache 1
cache size 4.1 kB
max cache size 5.0 GB
$ ccache g++ -c -Ipcg-cpp-0.98.1/include pcg-example.cpp
$ ccache -s
cache directory /home/vedranmiletic/.ccache
primary config /home/vedranmiletic/.ccache/ccache.conf
secondary config (readonly) /etc/ccache.conf
stats updated Thu Feb 6 11:39:45 2020
cache hit (direct) 0
cache hit (preprocessed) 0
cache miss 2
cache hit rate 0.00 %
cleanups performed 0
files in cache 2
cache size 8.2 kB
max cache size 5.0 GB
The debug log file shows a different hash each time:
[2020-02-06T11:39:35.920164 14858] === CCACHE 3.7.7 STARTED =========================================
[2020-02-06T11:39:35.920164 14858] Config: (default) base_dir =
[2020-02-06T11:39:35.920164 14858] Config: (default) cache_dir = /home/vedranmiletic/.ccache
[2020-02-06T11:39:35.920164 14858] Config: (default) cache_dir_levels = 2
[2020-02-06T11:39:35.920164 14858] Config: (default) compiler =
[2020-02-06T11:39:35.920164 14858] Config: (default) compiler_check = mtime
[2020-02-06T11:39:35.920164 14858] Config: (default) compression = false
[2020-02-06T11:39:35.920164 14858] Config: (default) compression_level = 6
[2020-02-06T11:39:35.920164 14858] Config: (default) cpp_extension =
[2020-02-06T11:39:35.920164 14858] Config: (default) debug = false
[2020-02-06T11:39:35.920164 14858] Config: (default) depend_mode = false
[2020-02-06T11:39:35.920164 14858] Config: (default) direct_mode = true
[2020-02-06T11:39:35.920164 14858] Config: (default) disable = false
[2020-02-06T11:39:35.920164 14858] Config: (default) extra_files_to_hash =
[2020-02-06T11:39:35.920164 14858] Config: (default) hard_link = false
[2020-02-06T11:39:35.920164 14858] Config: (default) hash_dir = true
[2020-02-06T11:39:35.920164 14858] Config: (default) ignore_headers_in_manifest =
[2020-02-06T11:39:35.920164 14858] Config: (default) keep_comments_cpp = false
[2020-02-06T11:39:35.920164 14858] Config: (default) limit_multiple = 0.8
[2020-02-06T11:39:35.920164 14858] Config: (environment) log_file = /tmp/cache-minimal.debug
[2020-02-06T11:39:35.920164 14858] Config: (default) max_files = 0
[2020-02-06T11:39:35.920164 14858] Config: (default) max_size = 5.0G
[2020-02-06T11:39:35.920164 14858] Config: (default) path =
[2020-02-06T11:39:35.920164 14858] Config: (default) pch_external_checksum = false
[2020-02-06T11:39:35.920164 14858] Config: (default) prefix_command =
[2020-02-06T11:39:35.920164 14858] Config: (default) prefix_command_cpp =
[2020-02-06T11:39:35.920164 14858] Config: (default) read_only = false
[2020-02-06T11:39:35.920164 14858] Config: (default) read_only_direct = false
[2020-02-06T11:39:35.920164 14858] Config: (default) recache = false
[2020-02-06T11:39:35.920164 14858] Config: (default) run_second_cpp = true
[2020-02-06T11:39:35.920164 14858] Config: (default) sloppiness =
[2020-02-06T11:39:35.920164 14858] Config: (default) stats = true
[2020-02-06T11:39:35.920164 14858] Config: (default) temporary_dir =
[2020-02-06T11:39:35.920164 14858] Config: (default) umask =
[2020-02-06T11:39:35.920372 14858] Command line: ccache g++ -c -Ipcg-cpp-0.98.1/include pcg-example.cpp
[2020-02-06T11:39:35.920397 14858] Hostname: hephaestus
[2020-02-06T11:39:35.920420 14858] Working directory: /home/vedranmiletic
[2020-02-06T11:39:35.920478 14858] Source file: pcg-example.cpp
[2020-02-06T11:39:35.920488 14858] Object file: pcg-example.o
[2020-02-06T11:39:35.920510 14858] Trying direct lookup
[2020-02-06T11:39:35.920559 14858] Looking for object file hash in /home/vedranmiletic/.ccache/0/d/82650d7ebf4afae8251b89938339df-280.manifest
[2020-02-06T11:39:35.920575 14858] No such manifest file
[2020-02-06T11:39:35.920584 14858] Did not find object file hash in manifest
[2020-02-06T11:39:35.921046 14858] Running preprocessor
[2020-02-06T11:39:35.921070 14858] Executing /usr/bin/g++ -Ipcg-cpp-0.98.1/include -E pcg-example.cpp
[2020-02-06T11:39:36.015075 14858] Found __DATE__ in pcg-cpp-0.98.1/include/pcg_extras.hpp
[2020-02-06T11:39:36.015109 14858] Found __TIME__ in pcg-cpp-0.98.1/include/pcg_extras.hpp
[2020-02-06T11:39:36.015115 14858] Disabling direct mode
[2020-02-06T11:39:36.015660 14858] Got object file hash from preprocessor
[2020-02-06T11:39:36.015688 14858] Object file /home/vedranmiletic/.ccache/1/8/f93581b7617650b562067786ae323b-1161376.o not in cache
[2020-02-06T11:39:36.015700 14858] Running real compiler
[2020-02-06T11:39:36.015816 14858] Executing /usr/bin/g++ -Ipcg-cpp-0.98.1/include -c -o pcg-example.o pcg-example.cpp
[2020-02-06T11:39:36.535746 14858] Unlink /home/vedranmiletic/.ccache/1/8/f93581b7617650b562067786ae323b-1161376.o.tmp.stdout.hephaestus.14858.mXEZFI
[2020-02-06T11:39:36.535816 14858] Unlink /home/vedranmiletic/.ccache/1/8/f93581b7617650b562067786ae323b-1161376.o.tmp.stderr.hephaestus.14858.o0IuNH
[2020-02-06T11:39:36.535879 14858] Copying pcg-example.o to /home/vedranmiletic/.ccache/1/8/f93581b7617650b562067786ae323b-1161376.o via /home/vedranmiletic/.ccache/1/8/f93581b7617650b562067786ae323b-1161376.o.hephaestus.14858.DV8O0G (uncompressed)
[2020-02-06T11:39:36.535946 14858] Stored in cache: pcg-example.o -> /home/vedranmiletic/.ccache/1/8/f93581b7617650b562067786ae323b-1161376.o (copied)
[2020-02-06T11:39:36.536325 14858] Result: cache miss
[2020-02-06T11:39:36.536348 14858] Acquired lock /home/vedranmiletic/.ccache/1/stats.lock
[2020-02-06T11:39:36.536397 14858] Releasing lock /home/vedranmiletic/.ccache/1/stats.lock
[2020-02-06T11:39:36.536402 14858] Unlink /home/vedranmiletic/.ccache/1/stats.lock
[2020-02-06T11:39:40.335718 14864] === CCACHE 3.7.7 STARTED =========================================
[2020-02-06T11:39:44.999691 14865] === CCACHE 3.7.7 STARTED =========================================
[2020-02-06T11:39:44.999691 14865] Config: (default) base_dir =
[2020-02-06T11:39:44.999691 14865] Config: (default) cache_dir = /home/vedranmiletic/.ccache
[2020-02-06T11:39:44.999691 14865] Config: (default) cache_dir_levels = 2
[2020-02-06T11:39:44.999691 14865] Config: (default) compiler =
[2020-02-06T11:39:44.999691 14865] Config: (default) compiler_check = mtime
[2020-02-06T11:39:44.999691 14865] Config: (default) compression = false
[2020-02-06T11:39:44.999691 14865] Config: (default) compression_level = 6
[2020-02-06T11:39:44.999691 14865] Config: (default) cpp_extension =
[2020-02-06T11:39:44.999691 14865] Config: (default) debug = false
[2020-02-06T11:39:44.999691 14865] Config: (default) depend_mode = false
[2020-02-06T11:39:44.999691 14865] Config: (default) direct_mode = true
[2020-02-06T11:39:44.999691 14865] Config: (default) disable = false
[2020-02-06T11:39:44.999691 14865] Config: (default) extra_files_to_hash =
[2020-02-06T11:39:44.999691 14865] Config: (default) hard_link = false
[2020-02-06T11:39:44.999691 14865] Config: (default) hash_dir = true
[2020-02-06T11:39:44.999691 14865] Config: (default) ignore_headers_in_manifest =
[2020-02-06T11:39:44.999691 14865] Config: (default) keep_comments_cpp = false
[2020-02-06T11:39:44.999691 14865] Config: (default) limit_multiple = 0.8
[2020-02-06T11:39:44.999691 14865] Config: (environment) log_file = /tmp/cache-minimal.debug
[2020-02-06T11:39:44.999691 14865] Config: (default) max_files = 0
[2020-02-06T11:39:44.999691 14865] Config: (/home/vedranmiletic/.ccache/ccache.conf) max_size = 5.0G
[2020-02-06T11:39:44.999691 14865] Config: (default) path =
[2020-02-06T11:39:44.999691 14865] Config: (default) pch_external_checksum = false
[2020-02-06T11:39:44.999691 14865] Config: (default) prefix_command =
[2020-02-06T11:39:44.999691 14865] Config: (default) prefix_command_cpp =
[2020-02-06T11:39:44.999691 14865] Config: (default) read_only = false
[2020-02-06T11:39:44.999691 14865] Config: (default) read_only_direct = false
[2020-02-06T11:39:44.999691 14865] Config: (default) recache = false
[2020-02-06T11:39:44.999691 14865] Config: (default) run_second_cpp = true
[2020-02-06T11:39:44.999691 14865] Config: (default) sloppiness =
[2020-02-06T11:39:44.999691 14865] Config: (default) stats = true
[2020-02-06T11:39:44.999691 14865] Config: (default) temporary_dir =
[2020-02-06T11:39:44.999691 14865] Config: (default) umask =
[2020-02-06T11:39:44.999905 14865] Command line: ccache g++ -c -Ipcg-cpp-0.98.1/include pcg-example.cpp
[2020-02-06T11:39:44.999925 14865] Hostname: hephaestus
[2020-02-06T11:39:44.999947 14865] Working directory: /home/vedranmiletic
[2020-02-06T11:39:44.999995 14865] Source file: pcg-example.cpp
[2020-02-06T11:39:45.000004 14865] Object file: pcg-example.o
[2020-02-06T11:39:45.000023 14865] Trying direct lookup
[2020-02-06T11:39:45.000062 14865] Looking for object file hash in /home/vedranmiletic/.ccache/0/d/82650d7ebf4afae8251b89938339df-280.manifest
[2020-02-06T11:39:45.000082 14865] No such manifest file
[2020-02-06T11:39:45.000090 14865] Did not find object file hash in manifest
[2020-02-06T11:39:45.000210 14865] Running preprocessor
[2020-02-06T11:39:45.000218 14865] Executing /usr/bin/g++ -Ipcg-cpp-0.98.1/include -E pcg-example.cpp
[2020-02-06T11:39:45.097064 14865] Found __DATE__ in pcg-cpp-0.98.1/include/pcg_extras.hpp
[2020-02-06T11:39:45.097099 14865] Found __TIME__ in pcg-cpp-0.98.1/include/pcg_extras.hpp
[2020-02-06T11:39:45.097105 14865] Disabling direct mode
[2020-02-06T11:39:45.097646 14865] Got object file hash from preprocessor
[2020-02-06T11:39:45.097670 14865] Object file /home/vedranmiletic/.ccache/f/1/9c68493f22eafad27987912faa2c81-1161376.o not in cache
[2020-02-06T11:39:45.097682 14865] Running real compiler
[2020-02-06T11:39:45.097803 14865] Executing /usr/bin/g++ -Ipcg-cpp-0.98.1/include -c -o pcg-example.o pcg-example.cpp
[2020-02-06T11:39:45.604277 14865] Unlink /home/vedranmiletic/.ccache/f/1/9c68493f22eafad27987912faa2c81-1161376.o.tmp.stdout.hephaestus.14865.YQFOyc
[2020-02-06T11:39:45.604348 14865] Unlink /home/vedranmiletic/.ccache/f/1/9c68493f22eafad27987912faa2c81-1161376.o.tmp.stderr.hephaestus.14865.la6gad
[2020-02-06T11:39:45.604427 14865] Copying pcg-example.o to /home/vedranmiletic/.ccache/f/1/9c68493f22eafad27987912faa2c81-1161376.o via /home/vedranmiletic/.ccache/f/1/9c68493f22eafad27987912faa2c81-1161376.o.hephaestus.14865.qo8Mbf (uncompressed)
[2020-02-06T11:39:45.604516 14865] Stored in cache: pcg-example.o -> /home/vedranmiletic/.ccache/f/1/9c68493f22eafad27987912faa2c81-1161376.o (copied)
[2020-02-06T11:39:45.605002 14865] Result: cache miss
[2020-02-06T11:39:45.605028 14865] Acquired lock /home/vedranmiletic/.ccache/f/stats.lock
[2020-02-06T11:39:45.605092 14865] Releasing lock /home/vedranmiletic/.ccache/f/stats.lock
[2020-02-06T11:39:45.605099 14865] Unlink /home/vedranmiletic/.ccache/f/stats.lock
[2020-02-06T11:39:46.647536 14871] === CCACHE 3.7.7 STARTED =========================================
When I seed and set the stream separately I get a different result than if I do that with a single call. Is that expected?
Example code:
#include <iostream>
#include "pcg_random.hpp"
int main(void) {
pcg64 rng;
rng.seed(20240409);
std::cout << rng << std::endl;
rng.set_stream(90404202);
std::cout << rng << std::endl;
rng.seed(20240409, 90404202);
std::cout << rng << std::endl;
return 0;
}
Output:
47026247687942121848144207491837523525 117397592171526113268558934119004209487 232695090976826000214660542883178056279
47026247687942121848144207491837523525 180808405 232695090976826000214660542883178056279
47026247687942121848144207491837523525 180808405 324486960932314774260885781527366674683
I would have expected the second and third line to be identical. While that is true for the stream indicator (second number), this is not the case for the RNG state (third number).
My simulations rely on a great number of random numbers. However, I want to be able uniquely restore a some given point. To this end I think it would be a good idea to store the current state
(it would not have to run through the entire sequence starting from some seed). However, it seems that the state
was not made public. I can image that that might have been on purpose. So: Am I overlooking something? If not, would you be open to making a public API to read/restore a state
?
pcg_random seems to have a bug at lines 1703-1704, namely,
these lines
typedef pcg_engines::ext_setseq_xsh_rr_64_32<6,16,true> pcg32_k2;
typedef pcg_engines::ext_oneseq_xsh_rs_64_32<6,32,true> pcg32_k2_fast;
should read
typedef pcg_engines::ext_setseq_xsh_rr_64_32<1,16,true> pcg32_k2;
typedef pcg_engines::ext_oneseq_xsh_rs_64_32<1,32,true> pcg32_k2_fast;
If the intention was to create a typedef for a 2-dimensionally equidistributed generator.
While investigating the source code, I've found several generators with the inconsistent names.
In file pcg_random.hpp on line 1724
typedef pcg_engines::ext_setseq_xsh_rr_64_32<6,16,true> pcg32_k64;
typedef pcg_engines::ext_mcg_xsh_rs_64_32<6,32,true> pcg32_k64_oneseq;
typedef pcg_engines::ext_oneseq_xsh_rs_64_32<6,32,true> pcg32_k64_fast;
pcg32_k64_oneseq
is based on the mcg
, and pcg32_k64_fast
is based on the oneseq
generator.
But on line 1732
typedef pcg_engines::ext_setseq_xsl_rr_128_64<5,16,true> pcg64_k32;
typedef pcg_engines::ext_oneseq_xsl_rr_128_64<5,128,true> pcg64_k32_oneseq;
typedef pcg_engines::ext_mcg_xsl_rr_128_64<5,128,true> pcg64_k32_fast;
pcg64_k32_oneseq
is based on the oneseq
generator, and pcg64_k32_fast
is based on the mcg
.
After further investigation, I discovered the following.
The base generator are declared as follows (line 1686)
typedef pcg_engines::setseq_xsh_rr_64_32 pcg32;
typedef pcg_engines::oneseq_xsh_rr_64_32 pcg32_oneseq;
typedef pcg_engines::unique_xsh_rr_64_32 pcg32_unique;
typedef pcg_engines::mcg_xsh_rs_64_32 pcg32_fast;
Where pcg32_oneseq
is based on oneseq
generator and pcg32_fast
is based on mcg
generator.
But multiple extended fast generators (pcg32_k2_fast
, pcg32_k64_fast
, pcg32_k1024_fast
, pcg32_c1024_fast
, pcg64_k1024_fast
, pcg64_c1024_fast
, pcg32_k16384_fast
) are based on oneseq
generator while other fast generators (pcg32_c64_fast
, pcg64_k32_fast
, pcg64_c32_fast
) are based on mcg
.
When using the _unique versions of pcg_random I get warnings when compiling with MSVC (Visual Studio 2017):
pcg_random.hpp(206): warning C4302: 'reinterpret_cast': truncation from 'const pcg_detail::unique_stream *' to 'unsigned long'
pcg_random.hpp(206): warning C4311: 'reinterpret_cast': pointer truncation from 'const pcg_detail::unique_stream *' to 'unsigned long'
The issue is on line 206 in pcg_random.hpp:
return itype(reinterpret_cast<unsigned long>(this) | 1);
Pointer truncation is probably no issue seeing as how it's used in pcg_random, so to turn off the warnings I'm disabling the warnings before including the pcg_random header:
#pragma warning(push)
#pragma warning(disable:4302)
#pragma warning(disable:4311)
#include <pcg_random.hpp>
#pragma warning(pop)
Perhaps a fix in your header would be better for the future.
The c++ version available for download at https://www.pcg-random.org/ is old (v0.98), and contains some bugs that have already been fixed in v0.98.1
It would be good to update or remove it, in order to avoid duplicate work and false bug reports.
Firstly, you should know that I am not "trolling" you, and that I'm writing this because I appreciate your work designing PRNGs (and a lot of your blog posts and similar have been valuable and enjoyable to me). I understand that trivial predictability seems to be your pet peeve. However, as far as I understand, "challenging" predictability comes at some cost for either statistical quality or speed, and you yourself acknowledge that
Algorithmic-complexity attacks aren't a major risk for our algorithms most of the time
So why not give users who don't need "challenging" predictability the option of using a PRNG that is tailored purely for excellent statistical quality, speed and small state size?
Furthermore, and I hope that writing this is a good idea, but I believe that your reasoning for making "challenging" predictability a goal at all is flawed, and your claims regarding the predictability of PCGs are deceptive.
The problem is that if someone needs to worry about algorithmic complexity attacks, PCG is not the right choice for them anyway, rather something like Randen seems to be a proper choice. The reason is that, as you yourself acknowledge, the PCG family is not cryptographically strong, i.e. the "challenging" predictability can not be relied on. In other words, trivial vs challenging predictability isn't a useful distinction. I'm pretty sure you already know that, unsurprisingly, a relatively efficient attack has already been described. Another thing that should be noted is that in your reasoning about the threat model you suppose that an attacker may only choose the easiest target; while in the real world determined and resourceful attackers with specific targets do exist. Such an attacker may even, for example, implement custom hardware to help with efficiently breaking the PRNG at hand.
To reiterate my point, one either worries about attacks on the PRNG, in which case they can use Randen or a CSPRNG; or they don't and they need good statistical quality and probably also speed, in which case they may use a PCG.
I hope you can appreciate my honesty, are not offended and your desire for making a trivially predictable PCG increases.
Looks like "expected" file containing the correct test results is missing:
[/havana/t/pcg/pcg-cpp]> make test
cd test-high; make
make[1]: Entering directory '/havana/t/pcg/pcg-cpp/test-high'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/havana/t/pcg/pcg-cpp/test-high'
cd sample; make
make[1]: Entering directory '/havana/t/pcg/pcg-cpp/sample'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/havana/t/pcg/pcg-cpp/sample'
cd test-high; make test
make[1]: Entering directory '/havana/t/pcg/pcg-cpp/test-high'
sh run-tests.sh
Performing a quick sanity check...
diff: expected: No such file or directory
diff: expected: No such file or directory
ERROR: Some tests failed.
make[1]: Leaving directory '/havana/t/pcg/pcg-cpp/test-high'
When trying to use pgc with a fully updated visual studio 2019 (version 16.7.5) I can
use generators as
// Make a random number engine
pcg64 rng(1234);
without any problem, but when trying out pcg64_c32
instead of pcg64
I see compile errors (I am using c++17 language mode but it doesn't matter). It complains about line 1246 in pcg_random.hpp. For some reason the table_mask constexpr seems to not evaluate to a constant.
I recently struggled getting the new C++11 random number distributions to work in my algorithm only to find that the distributions generate different sequences when run on different platforms (VS 2015 on windows gcc 5 on ubuntu 16). In my googling for an alternative, I came across PCG, which looks promising. Specifically, I am using std::uniform_int_distribution<unsigned int>
with a fixed seed for std::mt19937
. Will using PCG give me a reproducible sequence across ALL (most or even some) implementations?
Thanks,
Phil
Hi there!
I'm trying to use pcg64 in my program (first question: should I? When is pcg32 enough?)
When I do that I get the following compiler error with MSVC 19:
error C2678: binary '<<': no operator found which takes a left-hand operand of type 'dest_t' (or there is no acceptable conversion) [C:\Users\Eike\Documents\grazer\build\src\Auxillary\Mathfunctions\mathfunctions.vcxproj]
message : could be 'std::ostream &pcg_extras::operator <<(std::ostream &,uint8_t)' [found using argument-dependent lookup] [C:\Users\Eike\Documents\grazer\build\src\Auxillary\Mathfunctions\mathfunctions.vcxproj]
C:\Users\Eike\Documents\grazer\pcg\include\pcg_uint128.hpp(774,3): message : or 'pcg_extras::uint_x4<uint32_t,uint64_t> pcg_extras::operator <<<uint32_t,uint64_t>(const pcg_extras::uint_x4<uint32_t,uint64_t> &,const pcg_extras::bitcount_t)' [C:\Users\Eike\Documents\grazer\build\src\Auxillary\Mathfunctions\mathfunctions.vcxproj]
C:\Users\Eike\Documents\grazer\pcg\include\pcg_extras.hpp(425,1): message : or 'built-in C++ operator<<(UIntX2, UIntX2)' [C:\Users\Eike\Documents\grazer\build\src\Auxillary\Mathfunctions\mathfunctions.vcxproj]
with
[
UIntX2=uint64_t
]
Note that the line numbers are probably wrong, because I clang-formated the sourcecode inadvertedly.
The same compiles without issue with pcg32. I guess I would have to supply an additional overload for <<
but I don't know how. Can someone help me?
I notice that the C++ code forces two bits high:
engine(itype state = itype(0xcafef00dd15ea5e5ULL))
: state_(this->is_mcg ? state|state_type(3U)
: bump(state + this->increment()))
{
// Nothing else to do.
}
while the C code only forces one bit high:
inline void pcg_mcg_128_srandom_r(struct pcg_state_128* rng, pcg128_t initstate)
{
rng->state = initstate | 1u;
}
Is there a reason these two are not equivalent?
I also notice that this variant is missing from the C++ test suite (although since it is only ever initialised to 42 in the suite, no one would notice this difference).
I'd like to store my rng as a class member, as I have many methods that require it in my class. Something like:
class MyClass {
pcg32 rng;
// ...
MyClass() : rng(pcg_extras::seed_seq_from<std::random_device>{}) {}
}
leads to segfaults when I use, e.g. std::uniform_real_distribution
with rng
. I believe this is due to the fact that somewhere it tries to access a reference to the seed sequence, that was built in-place in the constructor! I'm not sure how to go around this... as you know, you can't store an std::random_device
, because its copy constructor is deleted. Can you provide a minimal example of how to use pcg "in oo-programming", or at least with classes and not just within main()
?
Hello,
the keyword _forceinline
is not recognized by MSVC2017, which I guess is when you disable the Microsoft specific extension to the ISO C++. Have to be replaced with double underscore prefix instead i.e. __forceinline
.
pcg-cpp/include/pcg_random.hpp
Line 96 in 0ca2e8e
Best regards.
The apparent reason is that when the engines output_previous
template parameter is true, the previous internal state generated with the previous increment is returned first. This is at least unintuitive, and I'd even consider it a bug. Maybe set_stream
should bump the internal state when output_previous
is true (though I have to admit I don't really see the purpose of outputting the previous state in any case, but I'm sure I'm just missing something?).
Example code:
pcg32 generator1{};
pcg32 generator2(generator1);
generator1.set_stream(0xF00BA8);
generator2.set_stream(0xBAA8F00);
// Uncomment to "Pump out" the stored internal state and make the assert pass
// generator1();
// generator2();
assert(generator1() != generator2()); // FAILS!
Also, if I use stream numbers 1 and 2, or for example 73, 146 or such simply generated seeds, the first two numbers generated are identical, but I suppose that's what's described shortly in https://www.pcg-random.org/posts/critiquing-pcg-streams.html in the STOP PRESS parentheses statement. I'd be more than happy to read a slightly longer discussion of how to safely initialize a number of streams, initially just given consecutive integers as instance numbers?
If seed
(or, for extended generators data
- it's fine to omit stream
) is omitted for any given constructor/seed call, the sanest default is to seed from /dev/urandom
by default. Only allow an insecurely-initialized RNG if some singleton enum is explicitly passed in place of the relevant argument.
This is a breaking change, but appropriate use of macros can let people hide if if they really need to. But chances are if it breaks anything, it was a bug in their code.
Note that the testsuite does rely on selfinit
since it never passes data
. But I don't like that. (I've replaced TWO_ARG_INIT
with ARG_INIT_COUNT
which can take any value from 0 to 3 (or conceptually more) and passes exactly that many arguments to RNG
's constructor)
Michael made some exploits to the DXSM output scrambler.
See: https://guru.multimedia.cx/how-correlated-is-pcg-dxsm-output/
I need to be able to send largest possible integer to a web application. This number will be passed back and forth to server.
template <typename T = pcg64_k1024_fast, typename R = int64_t> R computePCG() {
static thread_local T rng{pcg_extras::seed_seq_from<std::random_device>()};
return rng();
}
Using above code of PCG, On server I'm able to generate either 32-bit or 64-bit, However Json using 64 bit IEEE 754 - Only provides 53 bits precision for integer. So my options are:
OR
OR
Thank you for your time and looking into it.
Hello,
I have attempted to use the pcg number generator on visual studio but constantly faced with this error in this part of code.
#ifndef PCG_LITTLE_ENDIAN
#if defined(BYTE_ORDER)
#if BYTE_ORDER == ORDER_LITTLE_ENDIAN
#define PCG_LITTLE_ENDIAN 1
#elif BYTE_ORDER == ORDER_BIG_ENDIAN
#define PCG_LITTLE_ENDIAN 0
#else
#error BYTE_ORDER does not match a standard endian, pick a side
#endif
#elif LITTLE_ENDIAN || _LITTLE_ENDIAN
#define PCG_LITTLE_ENDIAN 1
#elif BIG_ENDIAN || _BIG_ENDIAN
#define PCG_LITTLE_ENDIAN 0
#elif __x86_64 || x86_64 || __i386 || i386
#define PCG_LITTLE_ENDIAN 1
#elif powerpc || POWERPC || ppc || PPC
|| m68k || mc68000
#define PCG_LITTLE_ENDIAN 0
#else
#error Unable to determine target endianness // error in this directive
#endif
#endif
The error is shown in this directive comment.
Would you be interested in a PR that makes most functions of this library constexpr? I haven't really investigated this yet, but it seems that one could just slab a CONSTEXPR_IN_ CPP14
-style macro in front of most functions.
The only tricky bit would be to make sure that this macro is restrictive enough that it doesn't accidentally add constexpr for a compiler / c++ version combination that nominally supports e.g. c++14 constexpr, but actually has an incomplete implementation which then results in a compilation error even when the function isn't used in a constexpr context.
Hi there!
I'm trying to use PCG for some hopefully cross-platform code. As I don't know where my code will be compiled and used, I would like to make sure that random numbers are as random-like as possible.
Therefore I thought, that seeding pcg32
and the like with both a std::random_device and
static_cast<std::random_device::result_type>(
std::chrono::high_resolution_clock::now()
.time_since_epoch()
.count())
but I have no idea how to do that. In addition I didn't gather from https://www.pcg-random.org/ how to seed the generators with deterministic data e.g. in order to make reproducible runs.
Could someone point me in the right direction? Even more appreciated would be some sample code like:
// add time here?
pcg_extras::seed_seq_from<std::random_device> seed_source;
/// add current time here?
pcg64 rng(seed_source);
which uses both std::random_device
and the current time in high resolution for entropy or even better with the additional option to provided entropy by the user.
I'm not sure, this is the right place for this, so feel free to send me elsewhere.
Any plan of proposing the PCG RNG to the C++ standard committee? The current RNGs in C++ are slow: https://en.cppreference.com/w/cpp/numeric/random.
First off: apologies if this is the wrong place to ask a usage-related question, I couldn't find any indication of where to ask for help on the PCG website.
The scenario is fairly simple: I am using multiple pcg32
instances (at least 1000, up to a million), each one of which is constructed with a different seed and an incremented stream value (1, 2, 3, etc.). The seeds are different between instances; for the sake of discussion, they can be assumed to be independently and uniformly sampled from the entire range of 64-bit integers.
Upon reviewing my code, someone mentioned that the use of different seeds was unnecessary in the presence of different streams. This sounded reasonable but some reading of the PCG blog made me wonder:
it may be easier than I had thought to make “nearby” streams with correlated initializations like a constant seed and streams of 1,2,3,4
So, the question: is there any advantage or disadvantage to my current PCG32 initialization scheme, over using a constant seed and a different stream for each instance?
See this commit: openclonk/openclonk@02c8dc9
Basically, the always_inline
attribute and the endianness detection don't work there.
When compiling with Visual Studio 16.7.3 on Windows 10 version 2004 I get #error: Unable to determine target endianness on line 74 of pcg_uint128.hpp.
Would it be appropriate to use BOOST_ENDIAN to replace the code in lines 55 - 76?
Would it be possible to create a new release please? The last one, 0.98.1, is very old (6 years) and broken with Visual Studio (2019 at least).
The makefile fails with the following compilation error:
mingw32-make
cd test-high; D:/mingw/winlibs-x86_64-posix-seh-gcc-13.2.0-llvm-17.0.5-mingw-w64ucrt-11.0.1-r3/mingw64/bin/mingw32-make.exe
The filename, directory name, or volume label syntax is incorrect.
mingw32-make: *** [makefile:25: all] Error 1
Things I noticed by hand:
struct rxs_mixin
has its function called output_rxs
rather than just plain output
, which would prevent it from being used as an output_mixin
if it were ever used.may_tock
is always false in the testsuite, so those branches aren't tested.x ? expr : 0
where x
can never be 0. Since the expression is usually a shift, it should probably be a safe_shift
function though.operator-
on extended generators after a step that can't be represented in a single itype
.Things I noticed missing from my port's coverage report:
pcg_engine
is ever tested (noticed because all the template-typedefs have to be functions in python)extended.advance_table
, both methods ininsideout
, all unoutput
functions, and unxorshift
are never tested.unique_stream.stream()
and oneseq_stream.stream()
Probably should be covered by operator ==
.engine.wrapped
engine.operator ==
and extended.operator ==
rxs_mixin
, rxs_m_mixin
, xsh_mixin
, xsl_mixin
extended.set
extended.advance
in the forward direction. Probably applies to engine
too ... and won't discard
malfunction?Some of these may be used in the samples, but those aren't run as part of the test suite, so can't be demonstrated to work.
Here's an example that won't build for pcg_t = ::pcg64
but works for pcg_t = ::pcg32
.
#include <random>
#include <sstream>
#include "third_party/pcg_random/include/pcg_random.hpp"
namespace {
using pcg_t = ::pcg64;
}
int main()
{
pcg_t rng(std::random_device{}());
std::stringstream out;
out << rng;
return 0;
}
I can verify similar issues in pcg-test.cpp if you add prints to std::cout or reads from std::cin.
What I believe is happening is: you're relying on Koenig lookup to find the definitions of operator<<
and operator>>
in namespace pcg_extras
. But pcg128_t
is typically a typedef for __uint128_t
; Koenig lookup doesn't work on typedefs (or more precisely, looks at the resolved type's namespace, not the typedef's.)
As I see it there are three possible fixes:
Move the operator definitions to the global namespace -- this is a bad idea, for one thing because anyone could have a conflicting definition.
Add a wrapper class around the typedef. This would solve the namespacing issue neatly; I don't know if we'd incur runtime cost from the compiler choking on the wrapper. Hopefully not but hard to prove.
Replace the operator<<
for pcg128_t
with similar free functions named (say) Input
and Output
and use SFINAE in the definitions of the engine operator<<
to prefer those if available. A bit clunky, but would work. (Also sort of necessary for the uint8_t
stuff, I think.)
I'm hacking together 3) to see how bad it looks, but not done yet.
Inheritance is often a code smell. Composition is usually cleaner.
Porting the full code to a language without templates was painfully difficult.
The least concern was that sometimes the names from the template parameters is used, whereas sometimes the typedef/constant in the class was used. To avoid, consider prefixing all template parameters with a _
.
The mixins were bad enough (especially since some have extra functions, like unoutput
), but inside_out
and extended
were atrocious. The frequent reliance on mutable references didn't help either.
I have a situation where I need to seed PCG from a random_device, perform some work and if later on if an investigation is needed to be able to generate the same randomness used for the given unit of work.
The following demo code does that and verifies the numbers generated are identical once the rng is reseeded - all that is good o_r at least seems "ok"_.
What I'm sort of confused with is the following:
i: 003157 state_size: 118 state: 0x34373032363234373638373934323132313834383134343230373439313833373532333532352031363633363437363738363637353336363735323933393837333638303333343331333134323120323333373034333838313634363535373131373135383634353733373431343636353934323032
i: 003158 state_size: 118 state: 0x34373032363234373638373934323132313834383134343230373439313833373532333532352031333030333935393939303235363433383336353032303136363737393735363637323235353920313230343430393334323834373531383136383133303336363435383435353632373833393137
i: 003159 state_size: 117 state: 0x343730323632343736383739343231323138343831343432303734393138333735323335323520363531363934333530333130383735333338303632333836383631373832383139313137393920323431303032363031393131313439313637363732303838353935373731303438343833313838
i: 003160 state_size: 117 state: 0x343730323632343736383739343231323138343831343432303734393138333735323335323520393935313338343230363636323432303630343532303532383139333536323536323231383720323332363033393736333335343535383934393736303732343437363538373339353737373333
i: 003161 state_size: 117 state: 0x343730323632343736383739343231323138343831343432303734393138333735323335323520333731363737353233333733363336363131373535313737323330363435323136303432313120333035303036363937303631363032353336303932373132363137353232333335343535383331
i: 003162 state_size: 118 state: 0x34373032363234373638373934323132313834383134343230373439313833373532333532352032323733313635303232313036373235353833383235313036353236393632363539353630393320313136373832343930373834353031343031343535303536383637313633393931383935323338
i: 003163 state_size: 117 state: 0x343730323632343736383739343231323138343831343432303734393138333735323335323520333236323138353338333539303336313134393633393134343832353439313932333534353631203838353632313331373737373230383139393639373631333233323433303635393832303738
i: 003164 state_size: 117 state: 0x343730323632343736383739343231323138343831343432303734393138333735323335323520313332383339363632303030323939323639383130343330323833363831303337313935333120313534343139303531303332323838343234343635333831353735383939353335363236303436
i: 003165 state_size: 117 state: 0x343730323632343736383739343231323138343831343432303734393138333735323335323520313034323438353737333232323436373138343037343833333634383936363533313639313339203337303138343536333937303235313736383835323038363133373433363832363238393034
i: 003166 state_size: 117 state: 0x343730323632343736383739343231323138343831343432303734393138333735323335323520323534303331333630383639343437303636383330353235323532373135383730333535393733203735373231313839343632343835313233383831363237303831383235363636373932313730
i: 003167 state_size: 117 state: 0x343730323632343736383739343231323138343831343432303734393138333735323335323520383231353738303835313334383637383139313637373731373235313635313139303430393320333334313230303333393734303932363836373031343330313939383130373733323739393530
i: 003168 state_size: 117 state: 0x343730323632343736383739343231323138343831343432303734393138333735323335323520343533333634333931343333343336313631313030333432393031323234363037373133343720323136333430323937393735353037323839323433323138333839353230323739373737343439
i: 003169 state_size: 118 state: 0x34373032363234373638373934323132313834383134343230373439313833373532333532352031343133323930303232343239363239393833373739303432393233363437323232373939303120323132393632303531323938303932303131373732303330303834323832313032363432343837
i: 003170 state_size: 118 state: 0x34373032363234373638373934323132313834383134343230373439313833373532333532352032343237323835353830363434383033363832343732323638323731393930333637343134393720313533363436393633333436373636343930383430363736383331313430343639353135393631
i: 003171 state_size: 117 state: 0x343730323632343736383739343231323138343831343432303734393138333735323335323520363834393934373638343332373537323537363739363839303336383233373135393239363120313330373838363933323238313439333338373834393838333739333938343833313934343532
i: 003172 state_size: 118 state: 0x34373032363234373638373934323132313834383134343230373439313833373532333532352031333733373636333631303336343634333437343832353131343038303531303238323535343520313334303531353339313533353539373536323933393433373634333435323332343831373237
i: 003173 state_size: 118 state: 0x34373032363234373638373934323132313834383134343230373439313833373532333532352032383235303038313431313632353637383236343135343031393631383139333331373739373920323834373539323533303533383235383134373939363737333235333033323036353030363830
i: 003174 state_size: 117 state: 0x343730323632343736383739343231323138343831343432303734393138333735323335323520323937373332373030313031383934343031353237323634313330393436393639373936303031203538383233313938323630383430323839323733363135343731323234393931373337303630
i: 003175 state_size: 117 state: 0x343730323632343736383739343231323138343831343432303734393138333735323335323520343039393832343930323034393138303630333633363032393534393732313833353833323120323231353830343932383634313233333330313437313537343935363634373935373639303934
i: 003176 state_size: 117 state: 0x343730323632343736383739343231323138343831343432303734393138333735323335323520353133343735353137353432343537343539333531323030363438383930393139323737303720333037303735353938373035313532343632313130343238333330333535383534363135373034
i: 003177 state_size: 116 state: 0x3437303236323437363837393432313231383438313434323037343931383337353233353235203231383132363332383330303839313736333230303639363033343033323632303631333230392037393333343434393335323837393638373636303731353539363633383830333939353831
i: 003178 state_size: 118 state: 0x34373032363234373638373934323132313834383134343230373439313833373532333532352032363133313632313631333532343338303834383632393638373839313039313130353937343320313532313031343530303337373235383132393035303031393935353531303433313737383333
i: 003179 state_size: 117 state: 0x343730323632343736383739343231323138343831343432303734393138333735323335323520383133393433393235303137303932313434373030353433363837303737373630383136393320313431353536393531303335383533383736333133323238343835323239323333383635333337
Are the above two situations normal or expected behavior?
Example code:
#include <cstdio>
#include <sstream>
#include <string>
#include <random>
#include <pcg/pcg_random.hpp>
std::string str2hex(const std::string& input)
{
static const char hex_digits[] = "0123456789ABCDEF";
std::string output;
output.reserve(input.length() * 2);
for (const unsigned char c : input)
{
output.push_back(hex_digits[c >> 4]);
output.push_back(hex_digits[c & 15]);
}
return output;
}
int main()
{
pcg_extras::seed_seq_from<std::random_device> seed_source;
constexpr auto max_num_samples = 10000000;
for (std::size_t i = 0; i < 1000000000; ++i)
{
pcg64 rng(seed_source);
std::string internal;
std::stringstream strm;
strm << rng;
rng_internal = strm.str();
printf("i: %06ld state_size: %ld state: 0x%s\n", i, internal.size(), str2hex(internal).c_str());
}
return 0;
}
I rewrote the 128 bit emulation class, but the tests won't compile. What's going on here:
../include/pcg_uint128.hpp:48:25: fatal error: no viable conversion from 'const pcg_extras::seed_seq_from<std::random_device>' to 'uint64_t' (aka 'unsigned long')
: UPPER(0), LOWER(rhs)
^ ~~~
../include/pcg_random.hpp:1288:21: note: in instantiation of function template specialization
'pcg_extras::uint128_t::uint128_t<pcg_extras::seed_seq_from<std::random_device> >' requested here
: baseclass(seedSeq)
^
../include/pcg_random.hpp:1296:20: note: in instantiation of function template specialization 'pcg_detail::extended<'\n', '\x80', pcg_detail::engine<unsigned long,
pcg_extras::uint128_t, pcg_detail::xsl_rr_mixin<unsigned long, pcg_extras::uint128_t>, false, pcg_detail::oneseq_stream<pcg_extras::uint128_t>,
pcg_detail::default_multiplier<pcg128_t> >, pcg_detail::engine<unsigned long, unsigned long, pcg_detail::rxs_m_xs_mixin<unsigned long, unsigned long>, true,
pcg_detail::oneseq_stream<unsigned long>, pcg_detail::default_multiplier<uint64_t> >, false>::extended<pcg_extras::seed_seq_from<std::random_device>, void>'
requested here
new (this) extended(std::forward<Args>(args)...);
^
./pcg-test-noadvance.cpp:87:13: note: in instantiation of function template specialization 'pcg_detail::extended<'\n', '\x80', pcg_detail::engine<unsigned long,
pcg_extras::uint128_t, pcg_detail::xsl_rr_mixin<unsigned long, pcg_extras::uint128_t>, false, pcg_detail::oneseq_stream<pcg_extras::uint128_t>,
pcg_detail::default_multiplier<pcg128_t> >, pcg_detail::engine<unsigned long, unsigned long, pcg_detail::rxs_m_xs_mixin<unsigned long, unsigned long>, true,
pcg_detail::oneseq_stream<unsigned long>, pcg_detail::default_multiplier<uint64_t> >, false>::seed<pcg_extras::seed_seq_from<std::random_device> >' requested
here
rng.seed(pcg_extras::seed_seq_from<std::random_device>());
^
This is Clang++ 4.0.0. I don't understand what pcg_random.hpp:1288 is doing.
The best pseudo-random number generator in the C++ standard library is probably the Mersenne twister, which has well-known disadvantages such as being pretty memory heavy.
Do you know if anyone has started writing an spec to add this to the C++ standard library?
Would you be interested in making a switch to cmake-based installation? For the project I am working on, I have started removing Makefile
s and writing CMakeLists.txt
files to
To make the sample functions and tests work (enforcing C++11 features in a portable way), I guess you need a CMake version of at least 3.1.3 (dates back to Feb 12, 2015).
Once finished, the build, test, and install processes will be portable among systems that support CMake. Moreover, all the *.cpp
files in test-high
directory will be removed, as they are simply compile definitions used on pcg-test.cpp
and pcg-test-noadvance.cpp
.
If you are interested, I can make a PR as soon as I finish writing the CMake scripts.
I've been poking at the test programs on Windows (Including replacing the current test-high/Makefile with a cmake file (which I can make a pull request for if there's interest), after getting tired of compiling random ones manually), and found two programs that fail to compile: check-pcg128_oneseq_once_insecure and check-pcg128_once_insecure. They both fail with the same error:
(ClCompile target) ->
c:\users\xxx\source\pcg-cpp\include\pcg_extras.hpp(534): error C2440: '<function-style-cast>': cannot
convert from 'pcg_extras::uint_x4<uint32_t,uint64_t>' to 'delta_t' [C:\Users\xxx\Source\pcg-cpp\test-hig
h\check-pcg128_oneseq_once_insecure.vcxproj]
I haven't looked any deeper into it yet, other than to see thatdelta_t
is an iterator difference type.
The good news is that all the tests that do compile pass, though since the size of various structures are different, there are some issues when diffing their output against the expected ones.
/usr/include/c++/10/bits/uniform_int_dist.h:249:4: error: ‘typedef long unsigned int pcg_detail::engine<long unsigned int, __int128 unsigned, pcg_detail::dxsm_mixin<long unsigned int, __int128 unsigned>, false, pcg_detail::oneseq_stream<__int128 unsigned>, pcg_detail::default_multiplier<__int128 unsigned> >::result_type’ is inaccessible within this context
Problematic commit: ffd522e
According to the GCC standard (§8.3.6): "The order of evaluation of function arguments is unspecified."
This causes a problem when PCG is initialized from another generator. GNU GCC evaluates from right (v9.3) while Apple Clang evaluates from left (v12).
The PCG is thus non-portable as with the same seed it produced different results on various platforms.
Problematic code:
pcg-cpp/include/pcg_random.hpp
Lines 514 to 521 in 5b5cac8
Line 520, 521. As we take two draws from the same generator, the order of evaluation matters. Clang thus generates a different stream than gcc.
It seems desirable to have a portable random number generation. (At least my use-case - scientific computation, experiment reproducibility requires it).
I must apologize if this issue reveals a misunderstanding on my side. Nevertheless, I've been banging my head against this for too long, so I'd like to go out public and ask for help.
I am working on a simulation code that is stochastic in nature, so requires drawing from PRNGs repeatedly. In an attempt to move forward in a tested and reproducible way, I discovered a problem with xcode recently, that I have boiled down to some simple example code:
//compile with: c++ -std=c++11 -o stdlib_20 stdlib_20.cpp
#include <iostream>
#include <iomanip>
#include <random>
int main(int argc, char *argv[])
{
std::mt19937 rng1(121);
std::mt19937 rng2(100);
std::normal_distribution<double> norm(5,2);
std::uniform_int_distribution<> uni(0,50);
std::cout.precision(8);
for( int i = 0;i<20;++i){
std::cout << std::setw(10) << norm(rng1) << " "
<< std::setw(4) << uni(rng2)
<< "\n";
}
return 0;
}
or using pcg of commit b656278
//compile with: c++ -std=c++11 -o pcg_20 pcg_20.cpp
#include <iostream>
#include <iomanip>
#include <random>
#include "pcg_random.hpp"
int main(int argc, char *argv[])
{
pcg32 rng1(121);
pcg32 rng2(100);
std::normal_distribution<double> norm(5,2);
std::uniform_int_distribution<> uni(0,50);
std::cout.precision(8);
for( int i = 0;i<20;++i){
std::cout << std::setw(10) << norm(rng1) << " "
<< std::setw(4) << uni(rng2)
<< "\n";
}
return 0;
}
The interesting observation is, that the above code gives different results with gcc 8.3.1
on fedora 29 and on osx with xcode 9.0.0, Target: x86_64-apple-darwin17.7.0
. Here is a diff of the top 10 numbers using pcg:
$ diff -y pcg.txt pcg_osx.txt|head -10
4.689577 32 | 7.1985629 49
7.1985629 48 | 4.689577 30
4.8603939 35 | 5.1719846 6
5.1719846 46 | 4.8603939 14
1.9203969 12 | 6.7887334 39
6.7887334 10 | 1.9203969 10
4.9383387 10 | 7.2118544 14
7.2118544 32 | 4.9383387 45
4.05167 33 | 2.6778881 35
2.6778881 21 | 4.05167 26
And using the stdlib:
$ diff -y stdlib.txt stdlib_osx.txt|head -10
3.7834751 27 | 6.5112185 8
6.5112185 34 | 3.7834751 24
3.4408029 14 | 5.2115202 3
5.2115202 21 | 3.4408029 39
8.6759752 21 | 6.0075102 23
6.0075102 26 | 8.6759752 15
7.7195293 43 | 6.0009774 48
6.0009774 7 | 7.7195293 10
8.1130049 0 | 6.1374016 30
6.1374016 7 | 8.1130049 34
The normal distribution (left column) appears to show consistent numbers but in an arbitrary ordering when comparing linux and osx. About the integer numbers I am quite uncertain what is going on. The question for me now is, is this an expected behavior/feature of PRNGs or is this a bug in xcode/gcc ?
Both pcg32_k2 and pcg32_k2_fast are defined with template parameter table_log2 = 6, making them 64-dimensionally equidistributed and requiring substantially more storage than the expected 2-dimensionally equidistributed generators. This is particularly obvious if we note that pcg32_k2 and pcg32_k64 have the same definition, as do pcg32_k2_fast and pcg32_k64_fast.
I'm submitting an issue rather than a PR because I'm not sure how advance_pow2 is chosen.
I'm working on an algorithm (procedural racetrack generator) whose outcome, in the general case, is solely based on the RNG outputs. But in some specific scenarios, I need to provide to user the ability to bypass some generation steps (e.g. terrain conformation) without affecting the rest of the generation process.
Let me make a simple practical example:
## Procedural racetrack ##
*
| Track shape generation | Terrain generation | Surfaces generation | Assets picking |
RNG stream | 1 2 3 | 4 5 6 7 | 8 9 | 0 |
## User-overridden racetrack ##
*
| Track shape generation | Terrain generation | Surfaces generation | Assets picking |
RNG stream | 1 2 3 | provided by user | 4 5 | 6 |
As you can see, in the User-overridden racetrack case, the fact that the Terrain generation step is bypassed (and so no RNG output is consumed) alters all the subsequent generation steps.
What I'm looking for is some sort of RNG synchronization operation (probably not the most corrected term to use...) to put in between the Terrain generation step and the Surfaces generation step to have the same RNG stream from the *
onwards, regardless if the terrain is provided by user or not. I thought about something like:
uint64_t seed = // initialized with racetrack ID
pcg32 rng(seed);
// Do Track shape generation step
// Do Terrain generation generation step
seed = seed + k; // where k is some constant
rng.seed(seed);
// Do Surfaces generation step
// Do Assets picking step
But I don't know if seed = seed + k;
is a valid reseeding approach (or there's something better) and if some value is better than others for k
.
[EDIT]
Forgot to state it, but the number of RNG outputs required for every generation step in not constant among different racetracks generation (and it isn't even predeterminable in advance).
As currently written, it seems impossible to use the library in a compiler in C++03 mode, this despite the library being otherwise a great addition to a C++ codebase as it should work similarly to the already available <random>
utilities in C++03's TR1. This also given that for example I was made aware of this library in a blog entry recommending better random utilities for C++ yet in these circumstances the better clear winner against this is a competing C solution that compiles and can be used generically without issues.
Upon initial inspection of an attempted compile with GCC 5 without -std=c++11
switch, the problems that prevent the library from being compiled in C++03 mode can be categorized in three types:
auto
to declare variables of types whose types are known or already named explicitly (eg.: in pcg_extras.hpp's operator<<
for stuff like char (in a call to narrow) or fmtflags (in a call to member flags).static_assert
or integral_constant
; backporting to C++03 would probably require changing calling sites using them (such as the static assert in pcg_random line 692, which can be made into a macro if the test expression is moved into an enum). In some cases such as nullptr
which even have an official backport this should be easily doable as most of these features are library-level solutions.constexpr
. These would probably require writing ad-hoc code to simulate the desired behaviour (some variables declared constexpr could be converted to enums, for example), or providing a more limited alternative.It would be interesting to see a more involved case study of what backporting to C++03 would require; in particular, while I don't have problems trying to edit the headers myself to seek out a better compile, I am not qualified for and wouldn't venture into ensuring that the statistical properties of the utilities are preserved.
(Disclainer: am the author of cxxomfort, a general backports library for C++, and am using the backports myself for the compile tests, so my experience might not reflect an attempted backport "from scratch")
Can anyone tell me if the current c++ implementation of the PCG random number generator thread safe?
The talk on youtube shows that the 64-bit version has twice the throughput of the 32-bit version (for processors that support 64-bit integers on hardware), which is pretty exciting. I wonder if PCG is suitable for extension to higher-throughput vectorized hardware instructions (ie. AVX/AVX2)?
https://github.com/imneme/pcg-cpp/blob/master/include/pcg_random.hpp
line 1575
template <bitcount_t table_pow2, bitcount_t advance_pow2,
typename baseclass, typename extvalclass, bool kdd>
void extended<table_pow2,advance_pow2,baseclass,extvalclass,kdd>::advance(
state_type distance, bool forwards)
extended doesn't have member advance error shown. advance_table though shows as member.
Four consecutive seeds give identical random numbers for the pcg64_fast or pcg32_fast generators. For example pcg64_fast(0)
, pcg64_fast(1)
, pcg64_fast(2)
, and pcg64_fast(3)
return identical states and thus identical random numbers.
See output of my test script (example.zip):
checking seed 0 to 9 using pcg32_fast..
0 0 3614609610 1032979711
1 0 3614609610 1032979711
2 0 3614609610 1032979711
3 0 3614609610 1032979711
4 0 517360375 2849173575
5 0 517360375 2849173575
6 0 517360375 2849173575
7 0 517360375 2849173575
8 0 3092770245 231179071
9 0 3092770245 231179071
checking seed 0 to 9 using pcg64_fast..
0 972365100324636832 3152476261539479119 4963323010661987954
1 972365100324636832 3152476261539479119 4963323010661987954
2 972365100324636832 3152476261539479119 4963323010661987954
3 972365100324636832 3152476261539479119 4963323010661987954
4 8681540523656324337 8610569632533855969 15689244027466136655
5 8681540523656324337 8610569632533855969 15689244027466136655
6 8681540523656324337 8610569632533855969 15689244027466136655
7 8681540523656324337 8610569632533855969 15689244027466136655
8 4621899917499390773 4965021727069323010 11849989275254633783
9 4621899917499390773 4965021727069323010 11849989275254633783
checking seed 0 to 9 using pcg32..
0 3894649422 2055130073 2315086854
1 1412771199 1791099446 124312908
2 3461116586 2338043315 3191287740
3 3053290573 2880989984 3620796526
4 78042152 3239916909 598054463
5 338748765 3035781544 893179696
6 2079632860 1396738611 3112634576
7 1273465047 4201302492 1760530922
8 1658943282 3425254359 1380307894
9 3396683593 298473608 3727095581
checking seed 0 to 9 using pcg64..
0 74029666500212977 8088122161323000979 16521829690994476282
1 16246141021062200314 13888980485107364105 1444523129010881979
2 15283991013767829887 16012073048563605758 18041754508879526868
3 4571984878009979848 4956887517419763765 10963035626649002760
4 8213965343793293256 14634166037439189285 7351202335824653137
5 12828375142235852897 3043909115430366615 12212956655512261919
6 14744360861768679421 7731410906820487750 9990277316766610057
7 2314236103276969522 16242248372244286679 9455988229017472731
8 6146847088143149122 105960750376085417 8469200379157136437
9 4176210987077628350 5147113462634521278 7929888373037025941
It appears that that seed = 4*n+i for i in {0,1,2,3} gives identical results for a fixed n for the two fast variants. Additionally, the 32 bit fast variant seems to start at zero.
Checking whether the problem remains for higher seeds:
pcg64_fast: All checked generators are memory identical for fixed seed // 4
pcg32_fast: All checked generators are memory identical for fixed seed // 4
I have incorporated the PCG family into an R package. On submission to CRAN I have been informed about a compilation error on Solaris:
In file included from dqrng.cpp:22:0:
../inst/include/pcg_random.hpp:1639:18: error: reference to ‘extended’ is ambiguous
using ext_std8 = extended<table_pow2, advance_pow2, BaseRNG,
^
In file included from /usr/include/math.h:382:0,
from /opt/csw/include/c++/5.2.0/cmath:44,
from /home/ripley/R/Lib32/Rcpp/include/Rcpp/platform/compiler.h:100,
from /home/ripley/R/Lib32/Rcpp/include/Rcpp/r/headers.h:48,
from /home/ripley/R/Lib32/Rcpp/include/RcppCommon.h:38,
from /home/ripley/R/Lib32/Rcpp/include/Rcpp.h:27,
from dqrng.cpp:18:
/usr/include/floatingpoint.h:71:18: note: candidates are: typedef unsigned int extended [3]
typedef unsigned extended[3];
^
I was able to circumvent this with a simple patch to use fully qualified names. Are you interested in a PR based on this patch?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.