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

MCG initialisation #45

Open
dhardy opened this issue Sep 27, 2018 · 1 comment
Open

MCG initialisation #45

dhardy opened this issue Sep 27, 2018 · 1 comment

Comments

@dhardy
Copy link

dhardy commented Sep 27, 2018

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).

@tbxfreeware
Copy link

tbxfreeware commented Jul 12, 2023

One issue that came up is a difference in the construction of MCGs (e.g. pcg64_fast):

* `pcg-c` uses `initstate | 1u` to seed an MCG [src](https://github.com/imneme/pcg-c/blob/master/include/pcg_variants.h#L812)

* `pcg-cpp` uses `state | 3u` to seed an MCG [src](https://github.com/imneme/pcg-cpp/blob/master/include/pcg_random.hpp#L484)

We decided to mirror the C version src, but standardisation or documentation by @imneme would be nice.

As this issue comes up from time to time, I'm going to risk a bit of futility by reviving an old topic. Apologies to @dhardy if he already knows the things I've written here.

The reason that PCG masks the two low-order bits of MCG state is because they never change. Setting them to a prescribed value makes them predictable.

You should test this yourself by writing a program that gets access to the internal state. The trick is to use operator<< to write the state out to a std::stringstream, and then read it back into an appropriately sized unsigned integer. (Otherwise, you can just make state_ public for a quick test.)

When I did this, I learned that the two low-order bits never change. During seeding, it does not matter what mask you use. Whatever values you set for the two LSBs, you'll be stuck with them. Random number generation does not modify them.

In the C++ implementation, they are both set to 1 (i.e., state | 3) because that version includes member function wrapped(), a function that returns true when a PCG engine has cycled through a full period. For an MCG, function wrapped() returns state_ == 3.

I don't know much about the C implementation, but I believe it does not have a wrapped() function. If you examine the code closely, however, you may discover some sort of similar test that relies on the initialization given by initstate | 1u .

So, what about the sequences generated by PCG? Given identical seeds, and four different engines that set the bits to 00, 01, 10, and 11, would the four engines produce identical output? The obvious answer is that depends how those bits are used by the permutation function. If they are used in any way at all, then the sequences would probably be different.

Does that affect the quality of the generated numbers? Hopefully not, but I am not qualified to answer the question. Presumably, Melissa O'Neill has chosen permutations where the answer is no.

Let us know if you find anything more...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants