Search code examples
c++randomgsl

How to adapt a C++-style random number engine for GSL (GNU Scientific Library)?


I have a PRNG that satisfies the requirements of a C++ random number engine, as described here. (It's from the PCG family). That is, an instance of the engine can be used by C++ standard library distribution classes to generate random numbers:

pcg_extras::seed_seq_from<std::random_device> seed_source;
pcg32 rng(seed_source);
std::uniform_real_distribution<double> uniformDist(0., 1.);
double randomNumber = uniformDist(rng);

I need to generate samples from a distribution not included in <random>, so I need to use a function from GSL. How do I use my C++ engine for this purpose? The GSL rng functions all require a const gsl_rng * as the first argument.

The default creation and usage of gsl_rng is as follows:

const gsl_rng_type * T;
gsl_rng * r;
gsl_rng_env_setup();
T = gsl_rng_default;
r = gsl_rng_alloc (T);
double u = gsl_rng_uniform (r);

To use my own engine, I believe that I will need to define a gsl_rng_type, as defined in "gsl/gsl_rng.h":

typedef struct
  {
    const char *name;
    unsigned long int max;
    unsigned long int min;
    size_t size;
    void (*set) (void *state, unsigned long int seed);
    unsigned long int (*get) (void *state);
    double (*get_double) (void *state);
  }
gsl_rng_type;

Am I correct in what I surmise is needed? Are there examples out there of how I might define a custom gsl_rng_type, especially the three necessary member functions? What might be my void *state?


Solution

  • I ended up doing the following to get things working. Note that my random number engine is a global variable in my program: g_rng.

    It's necessary to construct a struct to hold whatever state is relevant for the RNG. In my case, I simply hold a pointer to pcg32:

    // In some header file, e.g. rng.h
    typedef struct
    {
        pcg32 *rng;
    } gsl_pcg_state;
    

    Three functions, "set", "get", and "get_double" must be defined:

    // In some header file, e.g. rng.h
    static void gsl_pcg_set(void *state, unsigned long int seed)
    {
        ((gsl_pcg_state *)state)->rng = &g_rng;
        (void)seed;                 // unused
    }
    
    static unsigned long int gsl_pcg_get(void *state)
    {
        return (*((gsl_pcg_state *)state)->rng)();
    }
    
    static double gsl_pcg_get_double(void *state)
    {
        // Range [0, 1)
        return (*((gsl_pcg_state *)state)->rng)() / 4294967296.;
    }
    

    So then a gsl_rng_type can be instantiated:

    static const gsl_rng_type gsl_rng_pcg = {
        "pcg",
        0xffffffffUL,
        0,
        sizeof(gsl_pcg_state),
        &gsl_pcg_set,
        &gsl_pcg_get,
        &gsl_pcg_get_double
    };
    

    Within the main program, you would have...

    gsl_rng *r;
    r = gsl_rng_alloc(&gsl_rng_pcg);
    // one can now call various gsl_ran_[...] functions
    
    gsl_rng_free(r);
    

    Looking at gsl_rng_alloc as defined in rng.c from GSL confirms this is the right setup.