FFI introduction : Mersenne Twister

If your random number generator is not the Mersenne Twister, may I suggest to replace it with the Mersenne Twister of Makoto Matsumoto and Takuji Nishimura. This would significantly and instantly enhance the quality of your ML code that uses random numbers.

In SML#, this is done by the following trivial 15 minute process; no stub or data conversion is necessary.

Download Mersenne Twister

Go to the inventor's page, download the file mt19937ar.tgz, and un-tar it as follows:

 $ tar xvfz mt19937ar.tgz
 ./
 ./mt19937ar.c
 ./mt19937ar.out
 ./readme-mt.txt

(Comments) A faster and enhanced version SFMT has also been released. Using this should equally be simple (if your ML compiler is SML#). So I recommend to try using this version by yourself.

Compile Mersenne Twister as a dynamically linked library

In Linux OS, the following one line will do:

$ gcc -shared -o mt19937ar.so mt19937ar.c

The resulting file mt19937ar.so contains the following 8 functions.

void init_genrand(unsigned long s)
void init_by_array(unsigned long init_key[], int key_length)
unsigned long genrand_int32(void)
long genrand_int31(void)
double genrand_real1(void)
double genrand_real2(void)
double genrand_real3(void)
double genrand_res53(void)
int main(void)

Write an SML# structure.

To open the library mt19937ar.so, use the following FFI library functions:

# DynamicLink.dlopen;
val it = fn : string  -> unit ptr
# DynamicLink.dlsym;
val it = fn : unit ptr * string  -> unit ptr

dlopen(path) opens the file path path of a dynamically linked library. dlsym(ptr, s) searches the function name s in the library pointed by the handle ptr, and returns a pointer to the function.

The returned C function pointer is imported to SML# name space by the following SML# special construct:

exp _import : (t1,...,tn) -> t

It assumes exp to be a C pointer to the function that takes n arguments of types t1,...,tn and returns a value of type t, and converts it to an SML# function of the corresponding type

t1 * ... * tn -> t

So, you can simply write the following declaration that corresponds to the headers of 8 C functions in mt19937ar.

structure MT = struct
local
  val mtLib = DynamicLink.dlopen "./mt19937ar.so"
  fun find s = DynamicLink.dlsym (mtLib,s)
in
  val init_genrand = find "init_genrand" : _import (int) -> void
  val init_by_array = find "init_by_array"
                      : _import (int array,int) -> void
  val genrand_int32 = find "genrand_int32" : _import () -> int
  val genrand_int31 = find "genrand_int31" : _import () -> int
  val genrand_real1 = find "genrand_real1" : _import () -> real
  val genrand_real2 = find "genrand_real2" : _import () -> real
  val genrand_real3 = find "genrand_real3" : _import () -> real
  val genrand_res53 = find "genrand_res53" : _import () -> real
  val main = find "main" : _import () -> int
end
end

When this is evaluated, SML# print the following.

# use "mt.sml";
structure MT
: sig
    val genrand_int31 : unit -> int
    val genrand_int32 : unit -> int
    val genrand_real1 : unit -> real
    val genrand_real2 : unit -> real
    val genrand_real3 : unit -> real
    val genrand_res53 : unit -> real
    val init_by_array : int Array.array * int -> unit
    val init_genrand : int -> unit
    val main : unit -> int
  end
#

That's all. You are ready to use the Mersenne Twister by using these 8 functions with your favorite higher-order functions.

To test, try invoking the main function, which is the test program provided in the original package.

 # MT.main();
 1000 outputs of genrand_int32()
 1067595299  955945823  477289528 4107218783 4228976476
 ...
Last modified:2007/03/31 23:19:03
Keyword(s):
References:[Seamless Interoperability with C] [Program Examples]