`boost/random`

package, you will find a wealth of random number generators with different properties. In some cases you might want to test if your results depend on the specific implementation of the random number generator and whether some other generator might give better, or more dependable results. In this post I will write two wrapper classes that will allow any Boost random number generator to be used with QuantLib.
To understand the differences between the Boost generators and the generators of QuantLib, I will first analyse the anatomy of the Boost random number generators. Boost knows two types of random number generators, integral and floating point generators. As the name suggests, integral generators return integral values and floating point generators return floating point values. Let’s look at the integral generators first. The integral type that a generator returns can have different bit length. For example the `mt19937_64`

is a 64 bit version of the Mersenne twister with periodicity of \(2^{19937}-1\). Every generator class in the Boost library defines the `result_type`

as the type that the generator returns. For example `rand48`

contains the line

typedef boost::uint32_t result_type;

All Boost random number generators override the function operator to access the next random number.

result_type operator()();

This will generate and return the next number in the sequence. Random number generators all have a default constructor and a constructor that takes a seed. For all integral engines the seed type is the same as the result type. In addition there is a `seed`

method that re-initialises a random number generator with a seed. For example `rand48`

has the following constructors.

rand48(); rand48(result_type seed);

And the `seed`

method is defined as follows.

seed(result_type seed);

There is also a `seed`

function without arguments but we will only discuss the methods that are needed to create our generic wrapper class.

The aim is to write a generic wrapper around all of Boost’s random number generators. The idea is to follow the pattern used in the `Ranlux3UniformRng`

wrapper for Boost’s `ranlux64_3_01`

RNG. The difference will be that this wrapper will be templated with the type of the Boost RNG. I will start by creating the `GenericBoostIntRNG`

class with the template argument RNG

template<class RNG> class GenericBoostIntRNG {

Here `RNG`

is the tye of the Boost random number generator. `GenericBoostIntRNG`

needs to keep an instance of `RNG`

as private member. Here we encounter the first little hurdle. Getting the next random number from a Boost RNG will modify the object. The reasoning is that the internal state of the RNG is changed and this manifests itself in the fact that the function operator is non-const. QuantLib, on the other hand, expects that getting the next random number does not modify the generator. The `next`

method is a `const`

method in all the QuantLib RNGs. The solution is to make the Boost RNG a mutable member of the wrapper.

private: mutable RNG generator;

Next we have to declare two local types. QuantLib demands the `sample_type`

to define the return type of the next method. On the other hand, we also need to use `result_type`

of `RNG`

in many places and should therefore declare a shortcut.

public: typedef Sample<Real> sample_type; typedef typename RNG::result_type result_type;

To make sure that we use `GenericBoostIntRNG`

only with integral random number generators we should make sure that `result_type`

is an integer type. This can be done with `BOOST_STATIC_ASSERT`

and the standard `numeric_limits`

type.

BOOST_STATIC_ASSERT (std::numeric_limits<result_type>::is_integer);

At this point we should be defining the constructor. This is not straightforward and will require some extra code because we might need to create an automatic seed. We will postpone this and first focus on the easier methods that generate the random numbers.

Real nextReal() const { return (Real(generator()) + 0.5) / ceil_value; }

This method creates a random integer number and convert it into a `Real`

value. The `ceil_value`

will be one greater than the maximum number returned by the random number generator. We define it as a static private member of the class.

private: static Real ceil_value;

The value is set by using the `max`

function of the Boost random number generator.

template<class RNG> Real GenericBoostIntRNG<RNG>::ceil_value = Real(RNG::max()) + 1.0;

With this definition, the call to generator in the `nextReal`

method will always return a value from `0`

to `ceil_value-1`

. This means that the value returned by `nextReal`

will always fall within the closed interval.

$$\left[ \frac{0.5}{ceil}, \frac{ceil-0.5}{ceil}\right]$$

Mathematically this interval will never include 0 or 1 but we have to be careful when stating that the numerical value will lie in the open interval \((0,1)\). Rounding errors might cause the upper value to round to 1. This will be the case when the `Real`

type has less bits that the integral type of the Boost RNG. If we stick to using 32 bit Boost RNGs and define Real as 64 bit `double`

than all is fine and the numbers will always be less than 1. In any case the number will never be larger than 1 and an occasional value of 1 should not cause a problem. QuantLib random number generators are expected to define a `next`

method that returns a `sample_type`

. This is quickly done.

public: sample_type next() const { return sample_type(nextReal(), 1.0); }

Now we come back to creating the constructor for our generic wrapper class.

public: GenericBoostIntRNG(seed_type seed = 0) : generator(getSeed(seed)) {}

This does not look complicated. We are initializing the Boost object generator with a seed that we obtain from a method called `getSeed`

. The `getSeed`

method will have to be implemented. If we specify a seed that is non-zero that we will simply pass it on. Otherwise we will use the QuantLib `SeedGenerator`

class to create a suitable seed for the generator object.

private: result_type getSeed(result_type seed) { unsigned long s = (seed != 0 ? seed : SeedGenerator::instance().get()); return (result_type) (s % RNG::max()); }

In the first line inside the method we assign the seed to an `unsigned long`

. The `SeedGenerator`

class in QuantLib is defined as a singleton. A call to `SeedGenerator::instance().get()`

will return an `unsigned long`

seed value based on the current time. Because `unsigned long`

might be longer than the `result_type`

of the Boost RNG we have to restrict the values by taking the modulus with the maximum value of the RNG.

With this the generic wrapper is complete. I have tested it by plugging various Boost random number generators into the EquityOption example of QuantLib. When using the Boost’s mt19937 generator, I get identical results to the QuantLib MersenneTwisterUniformRng.

Boost also specifies a number of floating point random number generators. For completeness I have also created a wrapper for using these within QuantLib. Using the experience gained from GenericBoostIntRNG the implementation of GenericBoostFloatRNG is relatively straightforward.

template<class RNG> class GenericBoostFloatRNG { private: mutable RNG generator; static Real ceil_value; public: typedef Sample<Real> sample_type; typedef typename RNG::result_type result_type; typedef boost::uint32_t seed_type; BOOST_STATIC_ASSERT (std::numeric_limits<result_type>::is_float); GenericBoostFloatRNG(seed_type seed = 0) : generator(getSeed(seed)) {} sample_type next() const { return sample_type(nextReal(), 1.0); } result_type nextFloat() const { return generator(); } private: seed_type getSeed(seed_type seed) { unsigned long s = (seed != 0 ? seed : SeedGenerator::instance().get()); return (seed_type) (s % 0xffffffff); } };

There are three differences between this class and the `GenericBoostIntRNG`

class. First, the `nextFloat`

method can return the result of the generator directly. Second, we need to define the `seed_type`

separately from the `result_type`

. The `seed_type`

will be integral while the `result_type`

will be a floating point type. Third, the restriction of the seed to values accepted by the Boost RNG looks slightly different. All floating point random number generators accept `uint32`

as seed type. This means we can simply restrict the seed by applying a bit mask.

I hope someone will find this useful. The code for these wrapper classes is freely available under the MIT licence and available on Github.

Follow the author

]]>`std::vector`

with numbers. While random number generators can easily be used to create a random sequence, some algorithms, notably the low discrepancy sequence generators, can only be used to generate sequences and not individual random numbers.
QuantLib defines four pseudo-random number generators with uniform distribution. These are the Mersenne Twister 19937, the L’Ecuyer generator with added Bays-Durham shuffle, Knuth’s random number generator and Luescher’s Luxury random number generator. The last generator in this list is not implemented inside QuantLib but is a wrapper for the random number generator implemented in the **Boost Random** library. There are two versions of the luxury random number generator, Ranlux 3 and Ranlux 4. The second generator is slower than the first but provides more “luxury” (see M. Luescher, Computer Physics Communications, **79** (1994) pp 100-110). The number generators are defined in the following classes.

class MersenneTwisterUniformRng; class LecuyerUniformRng; class KnuthUniformRng; class Ranlux3UniformRng; class Ranlux4UniformRng;

All of these generators have constructors that take an optional initial seed as argument.

explicit MersenneTwisterUniformRng(unsigned long seed = 0); explicit LecuyerUniformRng(long seed = 0); explicit KnuthUniformRng(long seed = 0); explicit Ranlux3UniformRng(Size seed = 19780503u); explicit Ranlux4UniformRng(Size seed = 19780503u);

As you can see, different generators use slightly different integral types as seeds. The types depend on the internals of the generators. The default seed for the Mersenne Twister, L’Ecuyer and Knuth generators will cause a seed to be generated based on the system clock. The seed is calculated from the return value of the `time()`

function using the Mersenne Twister. All this is carried out inside the `SeedGenerator`

class.

While the details of the seeding might not be important to you there is one thing to take away from this. The seed is calculated in a deterministic way from the result of the `time()`

function but this function has a granularity of one second. If you run the same code multiple times during one second, the random number generator is likely to produce the same values.

`MersenneTwisterUniformRng`

provides another constructor that takes a vector of seeds which will initialise the internal states of the Mersenne Twister.

explicit MersenneTwisterUniformRng( const std::vector& seeds);

Each of the four random number generators has a method to get the next random number. The random number returned is a `Real`

number between, but not including, 0 and 1.

Sample<Real> next() const;

The return type is actually not just a simple `Real`

but a `Real`

value together with a weight wrapped inside the `Sample`

struct.

template <class T> struct Sample { typedef T value_type; Sample(const T& value, Real weight) : value(value), weight(weight) {} T value; Real weight; };

This struct is used throughout the QuantLib random number module to represent random numbers. For all samples returned by the random number generators, the `weight`

property is set to 1.0. To illustrate how to use the random number generators to create and print a sequence of random numbers, let’s look at a simple example.

MersenneTwisterUniformRng generator(12345); for (int i=0; i<100; ++i) { std::cout << generator.next().value << std::endl; }

The code above will create a Mersenne Twister and seed it with the number 12345. It will then create and print out 100 numbers.

You can easily create a random sequence from a random number generator with the generic `RandomSequenceGenerator`

. This class is templated with a random number generator class so it can use any generator to create the sequence.

template<class RNG> class RandomSequenceGenerator;

The class defines a type called `sample_type`

that defines the return type of the sequence generator. In general all sequence generators define the sample_type in the following way.

typedef Sample<std::vector<Real> > sample_type;

The generic random sequence generator has two constructors. The first argument of both constructors is the dimension, i.e. the length of the sequence that the generator will return. The second parameter is either a random number generator of the type specified by `RNG`

or a seed number that is used to initialise the random number generator. The seed is optional and defaults to zero if not specified.

RandomSequenceGenerator::RandomSequenceGenerator (Size dimensionality, const RNG& rng); RandomSequenceGenerator::RandomSequenceGenerator (Size dimensionality, BigNatural seed = 0);

Two methods allow access to the sequence.

const sample_type& nextSequence() const; const sample_type& lastSequence() const;

The method `nextSequence`

generates a new sequence with a length specified by dimensionality and returns a reference to it. Repeated access to the last sequence, without generating it again, is granted by `lastSequence`

. The following example shows how the sequence generator might be used.

RandomSequenceGenerator<MersenneTwisterUniformRng> generator(100, 12345); std::vector<Real> values = generator.nextSequence().value; for (int i=0; i<values.size(); ++i) { std::cout << values[i] << std::endl; }

All the previous random number and random sequence generators produce uniformly distributed random numbers. In many cases random numbers with different probability distributions are needed. The most common distribution is the Gaussian distributions. QuantLib provides two classes that generate Gaussian distributed random numbers from uniformly distributed random numbers. The `BoxMullerGaussianRng`

uses the Box Muller transform that generates pairs of normally distributed values from pairs of uniformly distributed values. Because the Box Muller transform needs to evaluate the square root and the logarithm for every number produced it can be slow for some applications. An alternative is the application of the central limit theorem, implemented in `CLGaussianRng`

. This generator uses the fact that the sum of 12 uniformly random numbers is already a good approximation for a normal distributed random variable. Both classes are templated with the type of the underlying random number generator.

template <class RNG> class BoxMullerGaussianRng; template <class RNG> class CLGaussianRng;

Both classes are simply constructed by passing the random number generator to the constructor.

explicit BoxMullerGaussianRng::BoxMullerGaussianRng (const RNG& uniformGenerator); explicit CLGaussianRng::CLGaussianRng (const RNG& uniformGenerator);

In addition, both classes follow the same pattern as the uniform random number generators for accessing the numbers and define the `next`

method.

Sample<Real> next() const;

In this way, both Gaussian generators can be plugged into the `RandomSequenceGenerator`

in the same way as any of the uniform generators. The following example will illustrate this.

typedef BoxMullerGaussianRng<MersenneTwisterUniformRng> BoxMuller; RandomSequenceGenerator<BoxMuller> generator(BoxMuller(100, 12345)); std::vector<Real> values = generator.nextSequence().value; for (int i=0; i<values.size(); ++i) { std::cout << values[i] << std::endl; }

The code above creates a vector filled with 100 normally distributed values. Inside the loop the values are then printed out.

For distributions other than Gaussian distributions QuantLib defines a general class called `InverseCumulativeRng`

. The class creates random numbers with an arbitrary distribution from its inverse cumulative distribution. Assume that the desired probability distribution of the random numbers is given by the function \(f(x)\). Then the cumulative distribution function is then given by

$$F(x) = \int_{-\infty}^x f(x’) dx’$$

All that is needed is a function object that evaluates the inverse of the cumulative distribution \(F^{-1}(x)\). The declaration of the `InverseCumulativeRng`

class looks like this.

template <class RNG, class IC> class InverseCumulativeRng { public: typedef Sample<Real> sample_type; typedef RNG urng_type; explicit InverseCumulativeRng(const RNG& uniformGenerator); sample_type next() const; };

The class `IC`

must have a default constructor and must overload the function operator to return the inverse cumulative distribution.

Real IC::operator()(Real x);

The following example may be helpful in illustrating how to use the `InverseCumulativeRng`

class. Let’s assume that you want to create a random variable with a probability distribution given by the logistic distribution with mean zero and scale parameter 1,

$$f(x) = \frac{1}{\left(e^{x/2} + e^{-x/2}\right)}$$

Then the cumulative distribution function is given by

$$F(x) = \int_{-\infty}^x \frac{1}{\left(e^{x’/2} + e^{-x’/2}\right)} dx’ = \frac{1}{1+e^{-x}}$$

The inverse of the function can be found by solving \(y=F(x)\) for \(x\) which results in

$$F^{-1}(y) = \mathrm{ln}\left(\frac{y}{1-y}\right)$$

All we need to do is to create a class that evaluates this function.

class ICLogistics { public: Real operator()(Real y) { return log(y/(1-y)); } };

Now we can define the following random number generator.

typedef InverseCumulativeRNG< MersenneTwisterUniformRng, ICLogistics > LogisticsRNG;

In the example above we can now simply replace `BoxMuller`

with `LogisticsRNG`

to produce a sequence of random numbers with the logistics distribution.

Follow the author

]]>