Top Banner
Made with OpenOffice.org 1 R と C/C++ の連携 福島真太朗 第 7 回 R 勉強会@東京 2010 年 7 月 24 日
22

Tokyo.R(#07)

Jan 15, 2015

Download

Technology

sfchaos

 
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Tokyo.R(#07)

Made with OpenOffice.org 1

Rと C/C++の連携

福島真太朗 第 7回 R勉強会@東京

2010年 7月 24日

Page 2: Tokyo.R(#07)

Made with OpenOffice.org 2

出身:埼玉県職業:コンサルタント 主に金融工学のデータ解析・モデル構築等使用言語:R, C, C++, Java, Awk, PerlTwitter: @sfchaos

自己紹介

Page 3: Tokyo.R(#07)

Made with OpenOffice.org 3

発表の流れ

1.本発表の目標2.RからのC/C++の利用3.C++からのRの利用

Page 4: Tokyo.R(#07)

Made with OpenOffice.org 4

1.本発表の目標

R C/C++

Rでは遅い処理をC/C++に任せて高速化したい

蓄積してきたC/C++のライブラリを

活用したい

Rと C/C++の連携させる基本的な方法を学ぶ

将来こんな問題に直面するかも・・・

Page 5: Tokyo.R(#07)

Made with OpenOffice.org 5

2.Rからの C/C++の利用

Rから C/C++を利用する方法として, 主に2種類の方法が用いられる.

.C()関数を利用する方法

.Call()関数を利用する方法

Page 6: Tokyo.R(#07)

Made with OpenOffice.org 6

2つの多項式

かけあわせる

この係数を求める

考える問題

Page 7: Tokyo.R(#07)

Made with OpenOffice.org 7

2.1 .C()関数を利用する方法

Rオブジェクト

Rオブジェクト

R C/C++共用

オブジェクト( または DLL)

C/C++コードの作成

SO(/DLL)の作成

SO(/DLL)のRへの読み込み

Rの SO呼び出し関数の作成

Rでの関数呼び出し

.C()関数

Page 8: Tokyo.R(#07)

Made with OpenOffice.org 8

1 extern "C" void convolve_c(double *a, int *na, double *b, int *nb, double *ab) 2 { 3 int nab = *na + *nb -1; 4 5 for (int i = 0; i < nab; ++i) ab[i] = .0; 6 for (int i = 0; i < *na; ++i) { 7 for (int j = 0; j < *nb; ++j) { 8 ab[i+j] += a[i] * b[j]; 9 }10 }11 }

引数はポインタ渡し(最後の引数がRに渡す値を 格納する配列の先頭ポインタ)

C++コードでは“extern C”をつける

関数の返り値は void型

Step1) C/C++コードの作成

Page 9: Tokyo.R(#07)

Made with OpenOffice.org 9

> dyn.load("conv_c.so")

convolve.r <- convolve.r <- function(a, b) .C("convolve", function(a, b) .C("convolve", as.double(a), as.integer(length(a)),as.double(a), as.integer(length(a)), as.double(b), as.integer(length(b)), as.double(b), as.integer(length(b)), res = double(length(a)+length(b)-1)) res = double(length(a)+length(b)-1))

[sfukushima@localhost test]$ R CMD SHLIB conv_c.cpp

conv.c <- function(a, b) .C("conv_c", as.double(a), as.integer(length(a)), as.double(b), as.integer(length(b)), res = double(length(a)+length(b)-1))

Step3) SO(/DLL)の Rへの読み込み

Step2) SO(/DLL)の作成

Step4) Rの SO(/DLL)呼び出し関数の作成・第1引数は,C/C++の関数名・引数の型は厳密に指定・最後の引数に返り値の型と サイズを指定

Page 10: Tokyo.R(#07)

Made with OpenOffice.org 10

> conv.c(1:10, 10:1)[[1]] [1] 1 2 3 4 5 6 7 8 9 10

[[2]][1] 10

[[3]] [1] 10 9 8 7 6 5 4 3 2 1

[[4]][1] 10

$result [1] 10 29 56 90 130 175 224 276 330 385 330 276 224 175 130 90 56 29 10

Step5) Rでの関数呼び出し

Page 11: Tokyo.R(#07)

Made with OpenOffice.org 11

2.2 .Call()関数を利用する方法

Rオブジェクト

Rオブジェクト

R C/C++共用

オブジェクト( または DLL)

C/C++コードの作成

SO(/DLL)の作成

SO(/DLL)のRへの読み込み

Rの SO呼び出し関数の作成

Rでの関数呼び出し

.Call()関数

Page 12: Tokyo.R(#07)

Made with OpenOffice.org 12

1: extern "C" void convolve_c(double *a, int *na, 1: extern "C" void convolve_c(double *a, int *na, double *b, int *nb, double *ab) double *b, int *nb, double *ab) 2: {2: { 3: 3: int nab = *na + *nb -1; int nab = *na + *nb -1; 4: 4: 5: 5: for (int i = 0; i < nab; ++i) ab[i] = .0; for (int i = 0; i < nab; ++i) ab[i] = .0; 6: 6: for (int i = 0; i < *na; ++i) { for (int i = 0; i < *na; ++i) { 7: for (int j = 0; j < *nb; ++j) ab[i+j] += a[i] * b[j];7: for (int j = 0; j < *nb; ++j) ab[i+j] += a[i] * b[j]; 8: 8: } } 9: }9: }

1 #include <Rdefines.h> 2 3 SEXP convolve_call(SEXP a, SEXP b) 4 { 5 PROTECT(a = AS_NUMERIC(a)); 6 PROTECT(b = AS_NUMERIC(b)); 7 int na = length(a), nb = length(b), nab = na + nb -1; 8 SEXP ab; 9 PROTECT(ab = NEW_NUMERIC(nab));1011 for (int i = 0; i < nab; ++i) REAL(ab)[i] = .0;1213 for (int i = 0; i < na; ++i) {14 for (int j = 0; j < nb; ++j) {15 REAL(ab)[i+j] += REAL(a)[i] * REAL(b)[j];16 }17 }18 UNPROTECT(3);19 return(ab);20 }

引数,返り値ともに型は”SEXP”

(Symbolic EXPresion)

SEXP型は Rがメモリを開放しないように

制御する

Rがメモリを開放できるようにする

Step1) C/C++コードの作成

変数の型を陽に指定する

Page 13: Tokyo.R(#07)

Made with OpenOffice.org 13

> dyn.load("conv_call.so")

conv.call <- function(a, b) .Call("conv_call", a, b)

[sfukushima@localhost test]$ R CMD SHLIB conv_call.cpp

convolve.r <- convolve.r <- function(a, b) .C("convolve", function(a, b) .C("convolve", as.double(a), as.integer(length(a)),as.double(a), as.integer(length(a)), as.double(b), as.integer(length(b)), as.double(b), as.integer(length(b)), res = double(length(a)+length(b)-1)) res = double(length(a)+length(b)-1))

> conv.call(1:10, 10:1) [1] 10 29 56 90 130 175 224 276 330 385 330 276 224 175 130 90 56 29 10

Step2) SO(/DLL)の作成

Step3) SO(/DLL)の Rへの読み込み

Step4) Rの SO(/DLL)呼び出し関数の作成

Step5) Rでの関数呼び出し

・第1引数は,C/C++の関数名・引数の型はしてしなくて良い!

Page 14: Tokyo.R(#07)

Made with OpenOffice.org 14

2.3 これまでのまとめ

.Call()関数はRから呼び出すときに、引数の型を明示的に指定しなくても良い→以降で述べるRcppパッケージは,この特徴をより良く活かしたパッケージ

C/C++コードの作成 引数の渡し方 考えられる活用シーン

.C()関数 ○ △(型を明示 )

既存のライブラリを用いるとき

.Call()関数△

(Rとの連携専用に作成)

○短いC/C++のインタフェースを書いて呼び出すとき

Page 15: Tokyo.R(#07)

Made with OpenOffice.org 15

2.4 Rcppパッケージ

C/C++コードの作成

SO(/DLL)の作成

SO(/DLL)のRへの読み込み

Rの SO呼び出し関数の作成

Rでの関数呼び出し

Rcppパッケージ

.Call()関数を用いて,C/C++を呼び出す場合のC/C++コードをもっと楽に書けないか?

Rオブジェクト

Rオブジェクト

R C/C++共用

オブジェクト( または DLL).Call()関数

Page 16: Tokyo.R(#07)

Made with OpenOffice.org 16

1 #include <R.h> 2 #include <Rinternals.h> 3 #include "Rcpp.h" 4 5 RcppExport SEXP conv_rcpp(SEXP a, SEXP b) 6 { 7 RcppVector<double> aa(a); 8 RcppVector<double> bb(b); 9 int nab = aa.size() + bb.size() - 1;1011 Rcpp::NumericVector ab(nab);12 for (int i = 0; i < nab; ++i) ab(i) = .0;13 for (int i = 0; i < aa.size(); ++i) {14 for (int j = 0; j < bb.size(); ++j) {15 ab(i + j) += aa(i) * bb(j);16 }17 }18 RcppResultSet rs;19 rs.add("ab", ab);20 return rs.getReturnList();21 }

#include <Rdefines.h> SEXP convolve_call(SEXP a, SEXP b) { PROTECT(a = AS_NUMERIC(a)); PROTECT(b = AS_NUMERIC(b)); int na = length(a), nb = length(b), nab = na + nb -1; SEXP ab; PROTECT(ab = NEW_NUMERIC(nab));

for (int i = 0; i < nab; ++i) REAL(ab)[i] = .0;

for (int i = 0; i < na; ++i) { for (int j = 0; j < nb; ++j) { REAL(ab)[i+j] += REAL(a)[i] * REAL(b)[j]; } } UNPROTECT(3); return(ab);}

Rcpp使用 Rcpp不使用

Page 17: Tokyo.R(#07)

Made with OpenOffice.org 17

1 #include <R.h> 2 #include <Rinternals.h> 3 #include <Rdefines.h> 4 #include <Rcpp.h> 5 #include <vector> 6 7 extern "C" SEXP rcpp_test(SEXP x, SEXP y) { 8 std::vector<double> xx(x), yy(y); 9 int n = xx.size();10 std::vector<double> res(n);1112 for (int i = 0; i < n; ++i) {13 double x = xx[i], y = yy[i];14 if (x < y) res[i] = x * x;15 else res[i] = -y * y;16 }17 return Rcpp::wrap(res);18 }

C++内部ではSTLの vectorなどを用いて最後にRcppの wrap関数で SEXPに変換することも可能

Page 18: Tokyo.R(#07)

Made with OpenOffice.org 18

C/C++コードで使用できるRの APIがある(“R.h”等を使用. “Writing R Extensions”Chapter.6参照 ).C++からの Rの利用方法として,今回はRInsideパッケージを紹介する.RInsideを用いると,C++から Rを呼び出して処理を行わせ,その結果をC++に戻すことが楽になる.

1 #include <iostream> 2 #include <algorithm> 3 #include "Rinside.h" 4 5 int main(int argc, char *argv[]) 6 { 7 const int dim = 10; 8 9 // 線形回帰分析に用いるデータの生成10 std::vector<double> x, y;11 for (int i = 0; i < dim;++i) {12 x.push_back(i); y.push_back(2*i);13 }14 // C++側でのデータの確認15 std::cout << "In c++: x" << std::endl;16 std::copy(x.begin(), x.end(),17 std::ostream_iterator<double>(std::cout, " "));18 std::cout << std::endl;19 std::cout << "In c++: v" << std::endl;20 std::copy(y.begin(), y.end(),21 std::ostream_iterator<double>(std::cout, " "));22 std::cout << std::endl;

となるベクトル をC++で生成

3. C/C++からの Rの利用

R C/C++

処理の呼び出し

計算結果の受け渡し

RInsideパッケージ

Page 19: Tokyo.R(#07)

Made with OpenOffice.org 19

1 #include <iostream> 2 #include <algorithm> 3 #include "Rinside.h" 4 5 int main(int argc, char *argv[]) 6 { 7 const int dim = 10; 8 9 // 線形回帰分析に用いるデータの生成10 std::vector<double> x, y;11 for (int i = 0; i < dim;++i) {12 x.push_back(i); y.push_back(2*i);13 }14 // C++側でのデータの確認15 std::cout << "In c++: x" << std::endl;16 std::copy(x.begin(), x.end(),17 std::ostream_iterator<double>(std::cout, " "));18 std::cout << std::endl;19 std::cout << "In c++: v" << std::endl;20 std::copy(y.begin(), y.end(),21 std::ostream_iterator<double>(std::cout, " "));22 std::cout << std::endl;

となるベクトル をC++で生成

3 C/C++からの Rの利用

Page 20: Tokyo.R(#07)

Made with OpenOffice.org 20

#include <iostream>#include <iostream>#include <algorithm>#include <algorithm>#include "RInside.h"#include "RInside.h"

int main(int argc, char *argv[])int main(int argc, char *argv[]){{ const int dim = 10;const int dim = 10;

// // 線形回帰分析に用いるデータの生成線形回帰分析に用いるデータの生成 std::vector<double> x, y;std::vector<double> x, y; for (int i = 0; i < dim;++i) {for (int i = 0; i < dim;++i) { x.push_back(i); y.push_back(2*i);x.push_back(i); y.push_back(2*i); }} // C++// C++ 側でのデータの確認側でのデータの確認 std::cout << "In c++: x" << std::endl;std::cout << "In c++: x" << std::endl; std::copy(x.begin(), x.end(),std::copy(x.begin(), x.end(), std::ostream_iterator<double>(std::cout, " "));std::ostream_iterator<double>(std::cout, " ")); std::cout << std::endl;std::cout << std::endl; std::cout << "In c++: v" << std::endl;std::cout << "In c++: v" << std::endl; std::copy(y.begin(), y.end(),std::copy(y.begin(), y.end(), std::ostream_iterator<double>(std::cout, " "));std::ostream_iterator<double>(std::cout, " ")); std::cout << std::endl;std::cout << std::endl;

23 // R側の分析データオブジェクトの生成24 RInside R(argc, argv);25 R.assign(x, "x"); R.assign(y, "y");26 // Rのコマンドの生成,実行27 std::string evalstr = "cat('In R: x \n'); print(x); \28 cat('In R: y \n'); print(y); \29 z <- lm(y~x); z <- z$fitted.values; \30 cat('In R: fitted.values \n'); print(z); z";31 SEXP ans;32 R.parseEval(evalstr, ans);3334 // C++においてRの返り値の格納35 RcppVector<double> vec(ans);36 std::vector<double> v = vec.stlVector();37 // 確認38 std::cout << "In c++: v" << std::endl;39 std::copy(v.begin(), v.end(),40 std::ostream_iterator<double>(std::cout, " "));41 std::cout << std::endl;

Rでもベクトル を生成

Rのコマンドの生成・受け渡し・実行

Page 21: Tokyo.R(#07)

Made with OpenOffice.org 21

[sfukushima@localhost test]$ ./rinside_testIn c++: x0 1 2 3 4 5 6 7 8 9 In c++: v0 2 4 6 8 10 12 14 16 18 In R: x [1] 0 1 2 3 4 5 6 7 8 9In R: y [1] 0 2 4 6 8 10 12 14 16 18In R: fitted.values 1 2 3 4 5 6 7 8 9 10 0 2 4 6 8 10 12 14 16 18 In c++: v5.2423e-16 2 4 6 8 10 12 14 16 18

C++でのベクトル の中身の確認

Rでのベクトル の中身の確認

Rの返り値の確認

C++においてRの返り値の確認

Page 22: Tokyo.R(#07)

Made with OpenOffice.org 22

参考文献

Dirk Eddelbuettel(2009):“Rcpp and Rinside: Easier R and C++ integration”, UseR! 2009 Presentation.

Dirk Eddelbuettel,Romain francois(2010):“Rcpp:Seamless R and C++”.

R Development Core Team(2010): “Writing R Extensions(ver.2.11.0)”.

John M.Chambers(2008): “Software for Data Analysis -Programming with R-”, Springer.