0

See the following program for g++.

#define seed1 0
#include <iostream>
#include <random>


int main()
{
    double mean = 0.0;
    double stddev  = 1.0;

    std::mt19937 generator1 (seed1);

    std::normal_distribution<double> normal(mean, stddev);
    std::cerr << "Normal: " << normal(generator1) << std::endl;
}

I want to get the state of generator1 (as a seed) and remove generator1 for later instantiate again the distribution with the new seed and go on in the place I left I want to put this code in a function and call it to generate Gaussian points in the start state I want. And at the end of the function save the state as a seed.

ChrisMM
  • 8,448
  • 13
  • 29
  • 48

3 Answers3

7

save the state as a seed

That will never happen, the state of a generator is much more than its seed.

However, generators (and distributions, which you ignored in your question) do provide functionality to store and retrieve their state through the << / >> operators respectively on streams:

stream << generator1 << normal;

And later:

mt19937 generator;
stream >> generator;

normal_distribution<double> distribution;
stream >> distribution;
Blindy
  • 65,249
  • 10
  • 91
  • 131
0

Let me also add, that generators and distributions are also constructible and copyable from itself, if there is a need to reuse it later.

#define seed1 0
#include <iostream>
#include <random>


int main()
{
    double mean = 0.0;
    double stddev  = 1.0;

    std::mt19937 generator1 (seed1);
    std::normal_distribution<double> normal(mean, stddev);

    std::cerr << "Normal: " << normal(generator1) << std::endl;

    std::mt19937 generator2 = generator1;
    std::normal_distribution<double> normal2 = normal;

    std::cerr << "Normal: " << normal(generator1) << std::endl;
    std::cerr << "Normal2: " << normal2(generator2) << std::endl;


// I want to get the state of generator1 (as a seed) and remove generator1 for later 
 //instantiate again the distribution with the new seed and go on in the place I left  
 // I want to put this code in a function and call it to generate Gaussian points in 
// the start state I want. And at the end of the function save the state as a seed.
}

So last lines print the same result, as states were initialized to equal values.

0

There is one way you could try. This involves saving the original seed, and counting how many times the generator is called. To restore the state, seed with the original seed and then call std::mt19937::discard()

Example:
#define seed1 0
#include <cassert>
#include <iostream>
#include <random>

// spoof a generator that counts the number of calls

class my_mt19937 : public std::mt19937 {
   public:
    result_type operator()() {
        ++call_count_;
        return std::mt19937::operator()();
    }

    void seed(result_type value = default_seed) {
        original_seed_ = value;
        std::mt19937::seed(value);
    }

    void discard(unsigned long long z) {
        call_count_ += z;
        std::mt19937::discard(z);
    }

    unsigned long long call_count() const noexcept { return call_count_; }

    result_type original_seed() const noexcept { return original_seed_; }

   private:
    result_type original_seed_ = default_seed;
    unsigned long long call_count_ = 0;
};

int main() {
    double mean = 0.0;
    double stddev = 1.0;

    my_mt19937 gen1;
    gen1.seed(seed1);

    const size_t N = 10'000;

    for (size_t i = 0; i < N; ++i) {
        my_mt19937 gen2;
        gen2.seed(gen1.original_seed());
        gen2.discard(gen1.call_count());

        if (gen2() != gen1()) {
            std::cout << "failed for i = " << i << "\n";
            return 1;
        }
    }

    // this extneds to distribution objects that 
    // use the generators

    std::normal_distribution<double> normal1;
    std::normal_distribution<double> normal2;

    for (size_t i = 0; i < N; ++i) {
        my_mt19937 gen2;
        gen2.seed(gen1.original_seed());
        gen2.discard(gen1.call_count());

        if (normal1(gen1) != normal2(gen2)) {
            std::cout << "failed for i = " << i << "\n";
            return 1;
        }
    }

    std::cout << "Success! Tested " << N << " values\n";

    return 0;
}

You can play further with the code on godbolt: https://godbolt.org/z/8j9onsezs

Michaël Roy
  • 6,338
  • 1
  • 15
  • 19
  • Why would you do that if you can simply store and restore the state of the generator? Your way is linear with the number of original calls. – Blindy Nov 22 '22 at 15:01
  • BecauseI didn't know, at the time. And since I had already written the code, I left it here. And no, discard() is not linear time for the mersenne twister engine std::mt19937 is based on, it is 0(1). – Michaël Roy Nov 22 '22 at 15:06
  • You are mistaking, `mt19937::discard(z)` is, and I quote, ["linear in z"](https://cplusplus.com/reference/random/mersenne_twister_engine/discard/). – Blindy Nov 22 '22 at 16:11