A potential pitfall when using boost::uniform_01

The Boost library collection has a highly useful random number library (Boost.Random). One of the classes in this library, boost::uniform_01, does however present a potential pitfall which I will briefly describe in this post. This pitfall is mentioned briefly in the library documentation but this post expands on this substantially.

The problem

The following pair of examples illustrate the problem with boost::uniform_01.

The quick, introductory example on the front page of random number library documentation demonstrates how to generate random integers between one and six and reads as follows:

// Note: *not* a compilable example, just fragment

boost::mt19937 rng;                 // produces randomness out of thin air
                                    // see pseudo-random number generators
boost::uniform_int<> six(1,6)       // distribution that maps to 1..6
                                    // see random number distributions
boost::variate_generator<boost::mt19937&, boost::uniform_int<> >
         die(rng, six);             // glues randomness with mapping
int x = die();                      // simulate rolling a die

This works fine.

Now, say instead of an integer between 1 and 6 you require a real number between 0 and 1. You may wish to use the class uniform_01 designed specifically for this purpose. Here is a an example that should compile and one that you would naturally try:

// Bojan Nikolic <bojan@bnikolic.co.uk>

#include <boost/random.hpp>

double rn(void)
{
  boost::mt19937 rng;
  boost::uniform_01<> zeroone;
  boost::variate_generator<boost::mt19937&,
    boost::uniform_01<> >
    gen(rng, zeroone);
  return gen()
}

This fails to compile with the following error message:

t1.cpp: In function ‘double rn()’:
t1.cpp:8: error: wrong number of template arguments (0, should be 2)
/usr/include/boost/random/uniform_01.hpp:30: error: provided for ‘template<class UniformRandomNumberGenerator, class RealType> class boost::uniform_01’
t1.cpp:8: error: invalid type in declaration before ‘;’ token
t1.cpp:10: error: wrong number of template arguments (0, should be 2)
/usr/include/boost/random/uniform_01.hpp:30: error: provided for ‘template<class UniformRandomNumberGenerator, class RealType> class boost::uniform_01’
t1.cpp:10: error: template argument 2 is invalid
t1.cpp:11: error: invalid type in declaration before ‘(’ token
t1.cpp:11: error: initializer expression list treated as compound expression
t1.cpp:12: error: ‘gen’ cannot be used as a function
t1.cpp:13: error: expected ‘;’ before ‘}’ token

The next logical step would be to provide the missing template parameter:

// Bojan Nikolic <bojan@bnikolic.co.uk>

#include <boost/random.hpp>

double rn(void)
{
  boost::mt19937 rng;
  boost::uniform_01<boost::mt19937> zeroone;
  boost::variate_generator<boost::mt19937&,
    boost::uniform_01<> >
    gen(rng, zeroone);
  return gen()
}

but that fails with a difficult-to-decipher error message:

t2.cpp: In function ‘double rn()’:
t2.cpp:8: error: no matching function for call to ‘boost::uniform_01<boost::random::mersenne_twister<unsigned int, 32, 624, 397, 31, 2567483615u, 11, 7, 2636928640u, 15, 4022730752u, 18, 3346425566u>, double>::uniform_01()’
/usr/include/boost/random/uniform_01.hpp:42: note: candidates are: boost::uniform_01<UniformRandomNumberGenerator, RealType>::uniform_01(UniformRandomNumberGenerator) [with UniformRandomNumberGenerator = boost::random::mersenne_twister<unsigned int, 32, 624, 397, 31, 2567483615u, 11, 7, 2636928640u, 15, 4022730752u, 18, 3346425566u>, RealType = double]
/usr/include/boost/random/uniform_01.hpp:31: note:                 boost::uniform_01<boost::random::mersenne_twister<unsigned int, 32, 624, 397, 31, 2567483615u, 11, 7, 2636928640u, 15, 4022730752u, 18, 3346425566u>, double>::uniform_01(const boost::uniform_01<boost::random::mersenne_twister<unsigned int, 32, 624, 397, 31, 2567483615u, 11, 7, 2636928640u, 15, 4022730752u, 18, 3346425566u>, double>&)
t2.cpp:10: error: wrong number of template arguments (0, should be 2)
/usr/include/boost/random/uniform_01.hpp:30: error: provided for ‘template<class UniformRandomNumberGenerator, class RealType> class boost::uniform_01’
t2.cpp:10: error: template argument 2 is invalid
t2.cpp:11: error: invalid type in declaration before ‘(’ token
t2.cpp:11: error: initializer expression list treated as compound expression
t2.cpp:11: error: cannot convert ‘boost::uniform_01<boost::random::mersenne_twister<unsigned int, 32, 624, 397, 31, 2567483615u, 11, 7, 2636928640u, 15, 4022730752u, 18, 3346425566u>, double>’ to ‘int’ in initialization
t2.cpp:12: error: ‘gen’ cannot be used as a function
t2.cpp:13: error: expected ‘;’ before ‘}’ token

A Solution

The reason for these error messages is that the design and interface of the boost::uniform_01 class is different to the other distribution classes. The primary differences in the interface are:

  • The constructor of boost::uniform_01 takes a random number generator as parameter
  • operator() of boost::uniform_01 does not take a random number generator as parameter.

As a result, there is a random number generator permanently attached to the boost::uniform_01 class and the use of boost::variate_generator is not necessary. The above example can therefore be reduced to the following compilable fragment:

// Bojan Nikolic <bojan@bnikolic.co.uk>

#include <boost/random.hpp>

double rn(void)
{
  boost::mt19937 rng;
  boost::uniform_01<boost::mt19937> zeroone(rng);
  return zeroone();
}

The Pitfall

A pitfall arises because the boost::uniform_01 constructor makes a new copy of the random number generator passed to it. Therefore, calls to the boost::uniform_01 do not advance the random number generator that the distribution was initialised with.

Here is an example how things could go wrong because of this:

// Bojan Nikolic <bojan@bnikolic.co.uk>

#include <iostream>

#include <boost/random.hpp>
#include <boost/format.hpp>

double rn(void)
{
  // The rng is made static to preserve its state across calls
  static boost::mt19937 rng(43);
  // but zeroone makes a copy which means the rng above *never advances*
  boost::uniform_01<boost::mt19937> zeroone(rng);
  return zeroone();
}

int main(void)
{
  std::cout<<boost::format("First draw: %g; second draw: %g") % rn() % rn()
       <<std::endl;
}

The result of running this code is the surprising:

First draw: 0.0114427; second draw: 0.0114427

I.e., not very random at all! This is however easily fixed by making the boost::uniform_01 object static:

// Bojan Nikolic <bojan@bnikolic.co.uk>

#include <iostream>

#include <boost/random.hpp>
#include <boost/format.hpp>

double rn(void)
{
  boost::mt19937 rng(43);
  static boost::uniform_01<boost::mt19937> zeroone(rng);
  return zeroone();
}

int main(void)
{
  std::cout<<boost::format("First draw: %g; second draw: %g") % rn() % rn()
       <<std::endl;
}

Summary

The interface of boost::uniform_01 is significantly different to other random number distributions in Boost.Random library. Therefore, read the documentation and watch out for the copy of the random number generator parameter.