ラベル R言語 の投稿を表示しています。 すべての投稿を表示
ラベル R言語 の投稿を表示しています。 すべての投稿を表示

2009年12月16日水曜日

R 言語の FORTRAN 拡張を試す (続編)

昨日のエントリ「R 言語 の FORTRAN 拡張を試す」 に続き、.Fortran 関数で外部ルーティンへの引数渡しについてもう少し調べた。

.Fortran には外部ルーティン名、外部ルーティンへの引数を渡す。デフォルトでは、外部ルーティンへ引数として渡す R オブジェクトは直接渡されず、一旦オブジェクトを複製し、複製されたオブジェクトのアドレスが外部ルーティンへ渡る。


例えば double に 1.0 をセットする外部ルーティン set_one を考える。

      subroutine set_one(a)
      real*8 a
      a = 1.d0
      end

x に 0 をセットして set_one に渡すと、

> x <- 0
> result <- .Fortran("set_one", x)
> x
[1] 0
> result[[1]]
[1] 1

x は 0 のままで、.Fortran が返したリストに 1 がセットされた。
.Fortran には DUP というオプションがありデフォルトは TRUE になっている。この場合引数として渡された x(のアドレス)が直接外部ルーティンに渡されず、x の複製が作成され、そのアドレスが外部ルーティンに渡される。その複製されたアドレスに 1 がセットされ、返り値リストに紐付けされる。

次に DUP = FALSE で .Fortran を呼ぶと

> x <- 0
> result <- .Fortran("set_one", x, DUP = FALSE)
> x
[1] 1
> result[[1]]
[1] 1

x 自体も 1 に書き変わっているのがわかる。

余談であるが、私の理解が正しければ、x も result[[1]] も同じアドレスを指しているはずである。にもかかわらず

> x <- 2
> result[[1]]
[1] 1

x を 2 にしても、 result[[1]] は 2 にならない。これは x が 2 という新しいオブジェクトを指し示すようになったからだ。昨日定義した "foo" でやってみれば明らか。

> x <- double(10)
> x
 [1] 0 0 0 0 0 0 0 0 0 0
> result <- .Fortran("foo", x, as.integer(10), 1, DUP=FALSE)
> x
 [1] 1 1 1 1 1 1 1 1 1 1
> result
[[1]]
 [1] 1 1 1 1 1 1 1 1 1 1
[[2]]
[1] 10
[[3]]
[1] 1
> x[2] <- 2
> x
 [1] 1 2 1 1 1 1 1 1 1 1
> result[[1]]
 [1] 1 2 1 1 1 1 1 1 1 1

今度は x と result[[1]] が連動しているのがわかる。

2009年12月15日火曜日

R 言語の FORTRAN 拡張を試す

試したのは一番簡単そうな .Fortran 関数。

まず、配列を受け取り、その中身を指定の値で埋める FORTRAN のサブルーチン "foo" を用意する。
      subroutine foo(a, n, x)
      implicit none
      real*8 a, x
      dimension a(n)
      integer n
      integer i
      do i = 1, n
         a(i) = x
      enddo
*      x = 100.d0
      end
mylib.f として保存し、 R CMD SHLIB mylib.f で共有ライブラリを作る
black-pearl:tmp shingo$ R CMD SHLIB mylib.f
gfortran -arch i386   -fPIC  -g -O2 -c mylib.f -o mylib.o
gcc -arch i386 -std=gnu99 -dynamiclib -Wl,-headerpad_max_install_names -mmacosx-version-min=10.4 -undefined dynamic_lookup -single_module -multiply_defined suppress -L/usr/local/lib -o mylib.so mylib.o -lgfortran -F/Library/Frameworks/R.framework/.. -framework R -Wl,-framework -Wl,CoreFoundation
ld: warning: duplicate dylib /usr/local/lib/libgcc_s.1.dylib
black-pearl:tmp shingo$ ls mylib.so
mylib.so
R を起動して、mylib.so を読み込む:
black-pearl:tmp shingo$ R
R version 2.9.2 (2009-08-24)
Copyright (C) 2009 The R Foundation for Statistical Computing
ISBN 3-900051-07-0

> dyn.load("mylib.so")
ここでさっき作った FORTRAN のサブルーチン "foo" を呼ぶ:
> result <- .fortran="" as.double="" as.integer="" foo="" p="">

サブルーチン "foo" は平たく言うと以下 3 つ受け取るだけなので、
  • 配列の先頭アドレス
  • 配列サイズが記録されているアドレス
  • 埋め尽くしたい数を記録しているアドレス
R 側の .Fortran の引数内で "foo" が使うメモリ領域を用意している:
  • as.double(1:100)
  • as.integer(100)
  • as.double(1.0)
結果はリストで帰ってきて、"foo" へ渡した引数領域が格納されていた
> class(result)
[1] "list"
> length(result)
[1] 3
> length(result[1])
[1] 1
> length(result[[1]])
[1] 100
> length(result[[2]])
[1] 1
> length(result[[3]])
[1] 1
> result[[1]]
  [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [38] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [75] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1