FFIの例:Mersenne Twister乱数を使おう

もしお使いの乱数発生関数がMersenne Twisterでないなら,ぜひ最強の乱数発生関数Mersenne Twisterに変更してみよう.

SML#では,そのFFI機能を活かして,以下の30分ほどの作業で,簡単に実現することができる.

Mersenne Twisterをダウンロード

Mersenne Twisterのページからmt19937ar.tgzファイル をダウンロードする.

これを以下のように展開する.

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

(注釈)最近より高速で改善されたSFMT がリリースされている。これを使うことも(もしお使いのMLコンパイラがSML#なら)以下同様簡単なはずなのでご自身でお試しになることを勧める。

Mersenne Twisterを動的リンクライブラリ用にコンパイル

mt19937ar.cがソースファイルである.このソースファイルには8つの関数が定義されている.そのヘッダ部分は以下の通りである.

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);

まずこれら関数を動的リンクライブラリとして使用できるようにコンパイルする.Linux系OSでは単に以下のコマンドを入力するだけでよい.

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

これによって,上の8つ関数が動的リンクライブラリファイルmt19937ar.soに登録される.

SML#モジュールの定義

動的リンクライブラリを利用するには,SML#の以下の関数を使用する.

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

dlopenは指定された名前の動的リンクライブラリのオープンを試み,成功すればそのハンドルへのポインタ(unit ptr)を返す.dlsym(ptr, s)はptrを動的リンクライブラリのハンドルへのポインターとみなし,その中のsの名前を持つ関数をサーチし見つかればその関数のポインタを返す.返された関数ポインタは,SML#が提供する特殊構文

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

でMLの関数としてインポートできる.型宣言は,この関数がt1,...,tnなるn個の引数を取り,帰り値の型がtであるCの関数であることを表す.

mt19937ar.soで定義されている関数を利用するSML#モジュールは,それぞれのCの型に対応した以下の宣言を行うだけでよい.

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

SML#はこの宣言を評価すると,通常のモジュールと同様に以下の情報をプリントする.

# 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
#

main関数は,テストプログラムである.この関数を起動すると,mt19937ar.outの内容と同一の以下のようにな乱数の例が表示されるはずである.

 # MT.main();
 1000 outputs of genrand_int32()
 1067595299  955945823  477289528 4107218783 4228976476
 ...

この後は,上記の8つの関数はSML#の型システムに従って,高階関数や多相関数と組み合わせ,自由に使うことができる.

Last modified:2007/03/31 23:20:28
Keyword(s):
References:[Cとのシームレスな連携] [プログラミング例]