This is an attempt to create a wrapper for
openturns using R. This is based on the wrapper template called
wrapper_calling_shell_command
available with openturns and somewhat inspired from
the scilab example. Wrappers allow you to call an external program as the function through which you propagate uncertainty with openturns, so that you can write you function in the language you are familiar with (R here) but still take advantage of open turns. This was done in fedora with R and open turns installed (see
this post for how to install open turns on a fedora 10 machine).
The first thing we need to do is to grab the template from the installed open turns.
$ mkdir ~/opwrappers
$ cp -fr /usr/local/share/openturns/WrapperTemplates/wrapper_calling_shell_command ~/opwrappers/rwrapper
$ cd ~/opwrappers/rwrapper/
$ ll
total 300
-rw-r--r-- 1 romain romain 27 2009-01-23 11:54 AUTHORS
-rwxr-xr-x 1 romain romain 1304 2009-01-23 11:54 bootstrap
-rw-r--r-- 1 romain romain 199260 2009-01-23 11:54 ChangeLog
-rw-r--r-- 1 romain romain 216 2009-01-23 11:54 code_C1.data
-rw-rw-r-- 1 romain romain 1594 2009-01-23 12:42 configure.ac
-rw-r--r-- 1 romain romain 18002 2009-01-23 11:54 COPYING
-rwxr-xr-x 1 romain romain 1794 2009-01-23 11:54 customize
-rw-r--r-- 1 romain romain 9498 2009-01-23 11:54 INSTALL
drwxr-xr-x 2 romain romain 4096 2009-01-23 11:54 m4
-rw-rw-r-- 1 romain romain 571 2009-01-23 12:42 Makefile.am
-rw-r--r-- 1 romain romain 447 2009-01-23 11:54 myCFunction.c
-rw-r--r-- 1 romain romain 455 2009-01-23 11:54 myCFunction.h
-rw-r--r-- 1 romain romain 0 2009-01-23 11:54 NEWS
-rw-r--r-- 1 romain romain 925 2009-01-23 11:54 README
-rwxrwxr-x 1 romain romain 435 2009-01-23 12:03 rwrapper.R
-rw-rw-r-- 1 romain romain 3722 2009-01-23 12:42 rwrapper.xml.in
-rw-rw-r-- 1 romain romain 1442 2009-01-23 12:42 test.py
-rw-rw-r-- 1 romain romain 9349 2009-01-23 12:42 wrapper.c
-rw-r--r-- 1 romain romain 27 2009-01-23 11:54 AUTHORS
The first thing to do is to
customize
the wrapper so that it is called
rwrapper
instead of the default
wcode
. This is achieved by the
customize
script:
$ ./customize rwrapper
The files
myCFunction.*
are useless and you can remove them at that point, we won't need the
code_C1.c
file either since we are going to write an R script instead.
$ rm myCFunction.*
$ rm code_C1.c
$ ll
total 288
-rw-r--r-- 1 romain romain 27 2009-01-23 11:54 AUTHORS
-rwxr-xr-x 1 romain romain 1304 2009-01-23 11:54 bootstrap
-rw-r--r-- 1 romain romain 199260 2009-01-23 11:54 ChangeLog
-rw-r--r-- 1 romain romain 216 2009-01-23 11:54 code_C1.data
-rw-rw-r-- 1 romain romain 1594 2009-01-23 12:42 configure.ac
-rw-r--r-- 1 romain romain 18002 2009-01-23 11:54 COPYING
-rwxr-xr-x 1 romain romain 1794 2009-01-23 11:54 customize
-rw-r--r-- 1 romain romain 9498 2009-01-23 11:54 INSTALL
drwxr-xr-x 2 romain romain 4096 2009-01-23 11:54 m4
-rw-rw-r-- 1 romain romain 571 2009-01-23 12:42 Makefile.am
-rw-r--r-- 1 romain romain 0 2009-01-23 11:54 NEWS
-rw-r--r-- 1 romain romain 925 2009-01-23 11:54 README
-rwxrwxr-x 1 romain romain 435 2009-01-23 12:03 rwrapper.R
-rw-rw-r-- 1 romain romain 3722 2009-01-23 12:42 rwrapper.xml.in
-rw-rw-r-- 1 romain romain 1442 2009-01-23 12:42 test.py
-rw-rw-r-- 1 romain romain 9349 2009-01-23 12:42 wrapper.c
Next, we need to write the R script that does the actual work, it needs to grab input file and output file, read data from the input file and write data to the output file. Something like
that :
#!/usr/bin/env Rscript
# grab arguments
argv <- commandArgs( TRUE )
datafile <- argv[1]
outfile <- argv[2]
# read data from data file
rl <- readLines( datafile )
extract <- function( index = 1 ){
rx <- sprintf( "^(I%d *= *)(.*)$", index )
as.numeric( gsub( rx, "\\2", grep(rx, rl, value = TRUE ) ) )
}
x1 <- extract( 1 )
x2 <- extract( 2 )
x3 <- extract( 3 )
out <- x1 + x2 + x3
cat( "O1 = ", out, sep = "", file = outfile )
Next, we need to modify the Makefile.am file so that the
make install
step copies the rwrapper.R file into the
wrappers/bin
directory later.
ACLOCAL_AMFLAGS = -I m4
wrapperdir = $(prefix)/wrappers
wrapper_LTLIBRARIES = rwrapper.la
wcode_la_SOURCES = wrapper.c
wcode_la_CPPFLAGS = $(OPENTURNS_WRAPPER_CPPFLAGS)
wcode_la_LDFLAGS = -module -no-undefined -version-info 0:0:0
wcode_la_LDFLAGS += $(OPENTURNS_WRAPPER_LDFLAGS)
wcode_la_LIBADD = $(OPENTURNS_WRAPPER_LIBS)
XMLWRAPPERFILE = rwrapper.xml
wrapper_DATA = $(XMLWRAPPERFILE)
EXTRA_DIST = $(XMLWRAPPERFILE).in test.py code_C1.data
execbindir = $(prefix)/bin
execbin_DATA = rwrapper.R
Then, we need to make a few changes to the
rwrapper.xml.in
file. Here is the definition of the output variable:
<variable id="O1" type="out">
<comment>Output 1</comment>
<unit>none</unit>
<regexp>O1\S*=\S*(\R)</regexp>
</variable>
You also need to add the
subst
tag in the output file definition (at least with this version of openturns) :
<!-- An output file -->
<file id="result" type="out">
<name>The output result file</name>
<path>code_C1.result</path>
<subst>O1</subst>
</file>
and then change the command that invokes the script as follows:
<command>Rscript @prefix@/bin/rwrapper.R code_C1.data code_C1.result</command>
Download the full
rwrapper.xml.in file
Once this is done (you can grab a
tar.gz of the wrapper at that stage) , you can compile the wrapper by following these steps:
$ ./bootstrap
$ ./configure --prefix=/home/romain/openturns --with-openturns=/usr/local
$ make
$ make install
If all goes well, you should have a
rwrapper.R
file in the
~/openturns/bin
directory and a file
rwrapper.xml
in the
~/openturns/wrappers
directory
Before trying the wrapper, we need to copy the input file in the directory where we are going to run openturns (say
/tmp
)
$ cp code_C1.data /tmp
$ cd /tmp
Now we are good to go and can start using the wrapper from open turns:
$ python
>>> from openturns import *
>>> p = NumericalPoint( (1,2,3))
>>> f = NumericalMathFunction( "rwrapper" )
>>> print f(p )
class=NumericalPoint name=Unnamed dimension=1 implementation=class=NumericalPointImplementation name=Unnamed dimension=1 values=[6]
>>> 1+2+3
6
The drawback of this approach is that each time the function needs to be evaluated, a new R session will be launched by Rscript, depending on the number of iterations we want to do this can affect seriously the run time of the study. A way to get around this is to use a single R session and let the wrapper communicate with it. I can see at least two ways to do it:
- by writing the function in python and let python communicate with R (using rpy for instance)
- by writing a c wrapper that would initialize a connection to an R server when the function is created, and call it whenever the function needs to be called
I'll try to tell these stories in another post