More random number generators in Lisp: using GSLL

In my previous post, I wrote an overview of this simple, but important, component which is the generation of pseudo-random numbers, using Common Lisp. However, sometimes you need a little bit more. Naturally, an implementation only offers one generator and no standard facilities exist to change. If you want a different generator the only options are to do it yourself or to use one from a library. Depending on one’s person needs, implementing our own generator might be or not an option. And using a library too. First, there are not many libraries containing different generators (at least, to the best of my knowledge). We can find code of other generators in Lisp but that’s very different from having a good, portable and complete library with everything (or most) we might need. As such, as far as I know, the best option is to use GSLL which allows us to use in Lisp the many things GSL has. GSL provides a unique set of pseudo-random numbers generators with the big advantage that they can be used later with random number distributions.

How can we use this facility that GSL/GSLL gives us? Well, first we need to install both libraries, as said before. As soon as everything is up and running, working with the library is not difficult. So far, I am not aware of a manual/tutorial for the Lisp side of GSL but as the website advises, we first look at the GSL documentation the name of the function that we want, then we use both gsl-lookup and documentation functions to learn about the Lisp equivalent. Looking at the examples might be also very useful. However, here I will focus just on the Lisp functions.

The first thing we need is to create an object that contains our pseudo-random generator. The object is named random-number-generator and we can create it using make-random-number-generator. This function requires an argument indicating the type of the generator. This is one of the big pluses of using GSLL. We can create several objects with different generators and the list provided by GSL is large (there is a function all-random-number-generators but it only returns the pointers, not the Lisp constant symbols). But for the moment, let’s focus on the essentials and assume that we also want a Mersenne Twister generator. This type of generator is represented by the constant +mt19937+. For example, if we want to define *rng* as a global variable that contains our generator from GSLL we only need to do this :

GSL> (defparameter *rng* (make-random-number-generator +mt19937+))
*RNG*

The variable *rng* now holds our generator. Anyway, this just creates our generator with the default seed. The default seed is contained in the constant +default-seed+ and defaults to the value of zero. In the previous example, the call might have been written like (make-random-number-generator +mt19937+ +default-seed+). If we want to seed the generator with a different seed value, we only need to add the seed, e.g., 1024, since it is an optional argument:

GSL> (defparameter *rng* (make-random-number-generator +mt19937+ 1024))
*RNG*

Contrary to the standard of Common Lisp, this allows to seed the generators with known seeds in the same way as in other languages. This could be useful if you want to compare and reproduce results with programs coded in languages other than Lisp. Having said that, we need to keep in mind the randomness issue of a user-specified seed.

We can now create pseudo-random generators but we don’t know how to produce the numbers. The basic function is get-random-number which receives as an argument the generator to be used and returns an integer. Although simple to use, the function returns integers from the interval [min,max] which is generator dependent. If we don’t know these bounds we can use the auxiliary functions rng-min and rng-max to obtain them. Using the MT generator with the default seed:

GSL> (rng-min *rng*)
0
GSL> (rng-max *rng*)
4294967295
GSL> (get-random-number *rng*)
4293858116
GSL> (get-random-number *rng*)
699692587
GSL> (get-random-number *rng*)
1213834231
GSL> (get-random-number *rng*)
4068197670

We can’t specify an upper bound like in the standard random function but it’s easy to make a function using get-random-number where we can set the limits from which we want pseudo-random numbers. Lucky for us, there is another way to produce our numbers by using the function sample. The major difference from get-random-number is that sample can produce floating point numbers and integers. In the first case, sample can generate numbers between 0.0 and 1.0 (exclusive). There is also the possibility to include or not 0.0. In the second case, sample generates integers between 0 and an upper bound given by a parameter (:upperbound). With this in mind, to decide how we want sample to behave the control flag must be one of the following: uniform, uniform>0 and uniform-fixnum. Examples:

Generating numbers between [0.0, 1.0):

GSL> (sample *rng* 'uniform)
0.23165654274635017d0
GSL> (sample *rng* 'uniform)
0.48497361433692276d0

Generating numbers between (0.0, 1.0):

GSL> (sample *rng* 'uniform>0)
0.9574769565369934d0
GSL> (sample *rng* 'uniform>0)
0.7443053431343287d0

Generating integers between 0 (inclusive ) and 100 (exclusive):

GSL> (sample *rng* 'uniform-fixnum :upperbound 100)
54
GSL> (sample *rng* 'uniform-fixnum :upperbound 100)
73

These are the main operations with random number generators using GSLL. As seen, the main advantage is to be able to create different types of generators and having an easier way to control the seed. There are other functions that operate on generators but less interesting. We have two functions that copy the state of a given generator: copy-to-destination and copy-making-destination. In short, these functions make an exact copy of a generator. The first function copies a generator into a given destination with the same type of the source. The second function does not need a destination. Examples:

GSL> (defparameter *rng2* (make-random-number-generator +mt19937+ 1024))
*RNG2*
GSL> (copy-to-destination *rng2* *rng*)
; No value
GSL> (get-random-number *rng*)
2781812667
GSL> (get-random-number *rng2*)
2781812667
GSL> (defparameter *rng3* (copy-making-destination *rng*))
*RNG3*
GSL> (get-random-number *rng2*)
1825252241
GSL> (get-random-number *rng3*)
1825252241

The other remaining functions are name, rng-state, size and get-random-state. The first function returns a string with the name of the generator, rng-state a pointer to the generator state, size the size of the pointer and get-random-state return the complete state of a given generator specified as a vector of bytes. And this is it!

Before concluding this overview of using pseudo-random numbers generators in Lisp using GSLL, just a few thoughts. Using this library seems a good option especially if generating random numbers is important in our programs. The reasons are evident: several generators to use, seed control, and library maturity (GSL). I have no idea of how GSLL affects performance but that is something to be explored and studied.

10 thoughts on “More random number generators in Lisp: using GSLL

  1. This is an excellent tutorial introduction to random number generators in GSLL. I have in mind that some day there would be a tutorial for usage of GSLL and I think a small variation on this would make a good chapter. Do you mind if I include this in the GSLL documentation (with credit of course)?

    There are a couple of small points:
    1) What do you mean by “Contrary to the standard of Common Lisp”? There is nothing in GSLL that changes the implementation’s conformance with the standard. Do you mean that GSL’s random generator implementation differs from the CL standard’s specification? If so, perhaps it’s better to say that.

    2) Note that copy-making-destination and copy-to-destination are not intended to be called directly by the GSLL user, and the symbols are not exported from the GSLL package. The proper interface is #’gsl:copy; it will select between the two based on the presence or absence of the second argument.

    Thanks for the great job.

    Liam

    1. Hi Liam,

      Thanks for the comment and especially for GSLL!

      Yes, you can include this in the documentation of GSLL. I will be very happy for it. This post was just my attempt to start learning GSLL and document for myself and anyone who might find it interesting. For a future chapter on a tutorial it still needs some work but that can also be done.

      Now, let me just answer your points, specifically 1). I guess I expressed myself in the wrong way or I was not clear enough. My intention was that the function “random” from the CL standard is different from the ones in GSLL since you cannot pass a seed number. In “random” you pass a state object, not a seed number. Just this. I didn’t mean that GSLL changed the implementation’s conformance, in fact, it never crossed my mind that paragraph could imply that. I hope it’s clear now!

      As for point 2) you are completely right. I was looking at GSLL source and looked at the functions I could use.

      I plan to write a little more about GSLL as soon as I start learning a bit more of it. In the near future I will be looking at the random distributions.

      All the best,
      Jorge

  2. […] Original post: More random number generators in Lisp: using GSLL « Jorge Tavares … […]

  3. Ahmed

    Hello
    great job , Thanks for the comments for Rondom number !
    My regards

  4. Faheem Mitha

    (sample *rng* ‘uniform) doesn’t work for me. I have to do

    (sample *rng* :uniform).

    There are examples in the source, in the ‘random’ subdirectory.

    1. This is an old post and probably things changed since then and I am not using GSLL anymore. It’s good to known that there are examples in the source.

      1. Frederic

        Dear Jorge,
        I am currently trying to figure out if I can use Common Lisp for scientific programming as well. Because I also need random number generators that can be seeded, I was happy to see your post. Thanks!
        But now I saw you’re not using GSLL anymore.
        Can you tell what you use as an replacement of GSLL and why?

      2. Hi Frederic,

        The two main reasons I stopped using GSLL was essentially that back then Quicklisp didn’t existed and it was hard for me to setup things in the clusters/other machines when I needed to run experiments. Also, calling GSLL generators was much slower when compared to SBCL. I don’t have numbers now but I remember that I did some benchmarks it my stuff and it was very significant (today it could be very different but it may also depend on your application). Anyway, I started using SBCL because its Mersenne Twister implementation is very fast and includes an extension that allows seeding. You need to call sb-ext:seed-random-state with the seed you want. If you can use only SBCL that’s the best option but if you need/want another implementation, then GSLL or implementing your own generator might be the only way. Some time ago I started a small library (but it was far from being complete/finished) for CL that implemented random number generators so that they would be portable across several CL implementations but it didn’t attract much interest and so I abandoned it.

        Hope this answers your question and thanks for your visit!

        Best,
        Jorge

  5. Frederic

    Thanks for the quick reply and your insights!
    I had found the sbcl extension for setting the seed state, but the prospect of using the same code on different machines and implementations, and still getting reproducable results sounded nice :)
    At least the manual for sbcl 1.1.4 states that:

    “The sequence of numbers produced by repeated calls to random starting with the same random state and using the same sequence of limit arguments is guaranteed to be reproducible only in the same version of SBCL on the same platform, using the same code under the same evaluator mode and compiler optimization qualities.”

    So on different machines (32/64bit), the same seed might result in different outcomes. But sbcl seems to be the fastest implementation, so for now I will stick with it, and keeping note which results were computed on which machine is useful anyway…

    1. Hi!

      Yes, the same results can only be guaranteed if it’s the same platform, same SBCL version, etc. Try to make some small experiments first to see if what kind of differences you’re getting on your settings. AT least you will know more or less what to expect. SBCL is a very good implementation for numerical computing so you won’t go wrong on that :-)

      Best,
      Jorge

Leave a comment