r..eal 0:0:3

# r..eal 0:0:3, 2011/12/22

A C-based Prolog interface to R.

## Introduction

This library enables the communication with an R process started as a shared library. It is the result of the efforts of two research groups that have worked in parallel. The syntactic emphasis on a minimalistic interface. A single predicate (<-/2,<-/1) channels the bulk of the interactions. In addition to using R as a shared library, r..eal uses the c-interfaces of Yap and R to pass objects in both directions. The usual mode of operation is to load Prolog values on to R variables and then call an R function on these values. The return value of the called function can be either placed on R variable or passed back to Prolog. It has been tested extensively on YAP 6.3.0 on Linux machines but it should also compile and work on MS operating systems and Macs. We hope it will soon be ported to the SWI engine.

The main modes for utilising the interface are

            <- +Rexpr
            <- +Rvar

Print Rvar or evaluate expression, Rexpr, in R

            +PLvar  <- +Rexpr
            +Rexpr1 <- +Rexpr2
            +Rvar   <- +PLval
            +PLvar  <- +Rvar

Evaluate Rexpr and pass result to PLvar.

Pass Rexpr1 <- Rexpr2 to R.

Pass PLval to Rvar (using the c-interface when Rvar is numeric, list or list of 2 levels). See data transfer below.

Assign contents of Rvar to PLvar using the c-interface.

## Syntax

There are syntactic conventions in R that make unparsable prolog code. Notably function and variable names are allowed to contain dots, square brackets are used to access parts of vectors and arrays and functions are allowed empty argument tuples. We have introduced relevant syntax which allows for easy transition between prolog and R. Prolog constructs are converted by the library as follows:

- `..` within atoms -> `.` (ex. `as..integer(c(1,2,3)) -> as.integer(c(1,2,3))` )
- `^[]` after atoms -> `[]` (ex. `a^[2] -> a[2]` )
- `(.)` at the end of atoms that are known R functions -> `()` (ex. `dev..off(.) -> dev.off()` )
- `[]` -> c() (which equal to R's NULL value)
- ( f(x) :- (..)) -> f(x) (...)
- Lists of lists of numbers are converted to matrices. All first level lists must have the same length.
- Filenames must be given as Prolog strings.
- R specific operators (eg. %*% should be quoted in Prolog.
- expressions that pose difficulty in translation can alwasy be passed as Prolog atoms or strings eg. 'inp$id'

## Data transfers

Following R convension we will talk about the type of and the class of data. i, defined by i <- as.integer(c(1,2,3)), is of class integer vector and type integer. R vectors are mapped to prolog lists and matrices are mapped to nested lists. The convention works the other way around too.

There are two ways to pass prolog data to R. The more efficient one is by using

     Rvar <- PLval

We will call this the low-level interface. It is implemented as C code and transfers via C data between R and Prolog. In what follows atomic PLval data are simply considered as singleton lists. Flat Plval lists are translated to R vectors and lists of one level of nesting to R matrices (which are 2 dimensional arrays in R parlance). The type of values of the vector or matrice is taken to be the type of the first data element of the Plval according to the following :

integer->integer
float->double
atom->char
boolean->logical

Booleans are represented in prolog as true/false atoms. Currently arrays of aribtrary dimensions are not supported in the low-level interface. Note that in R a scalar is just a one element vector. When passing Prolog objects to R, the user can specify the type of the R object by using the (:) operator (see real_type/1). For example:

    r <- float:[1,2,3].
    <- r.   				% don't be deceived :
    R <- r.

    i <- int:[1,2,3].
    <- i.
    I <- i.

In addition Prolog data can be passed through the expression mechanism. That is, data appearing in an arbitrary R expression will be parsed and be part of the long string that will be passed from Prolog to R for evaluation. This is only advisable for short data structures. For instance,

         tut_4a :-
              state <- c("tas", "sa",  "qld", "nsw", "nsw"),
              <- state.

Through this interface it is more convenient to represent R chars by Prolog list of codes, as in the above example.

## Examples

    ?- e <- numeric.
    yes
    ?- e^[3] <- 17.
    yes
    ?- Z <- e.
    Z = ['$NaN','$NaN',17.0]
    ?- e^[10] <- 12.
    yes
    ?- Z <- e.
    Z = ['$NaN','$NaN',17.0,'$NaN','$NaN','$NaN','$NaN','$NaN','$NaN',12.0]

    int :-
         i <- [1,2,3,4], I <- i, write( i(I) ), nl.

    bool_back :-
         t <- [1,2,3,4,5,1], s <- t==1, S <- s, write( s(S) ), nl.

    list :-
         a <- [x=1,y=0,z=3], A <- a, write( 'A'(A) ), nl.

    matrix :-
         a <- [[1,2,3],[4,5,6]], A <- a.

    expr1 :-
         a <- [[1,2,3],[4,5,6]], B <- a*3.

    expr2 :-
         a <- [[1,2,3],[4,5,6]], b <- 3, C <- a*b.

    use_module( library(apply_macros), [maplist/3] ).

    between_1_and( N, Bs ) :-
         findall( B, between(1,N,B), Bs ).

    :- use_module( library(apply_macros), [maplist/3] ).

    plot_cpu :-
         points <- [10,100,500,1000],
         points <- 'as.integer'(points * 10),
         <- points,
         Points <- points,
         write( points(Points) ), nl,
         findall( T, (member(P,Points),between_1_and(P,Long),
                       write( '.' ), flush_output,
                       statistics( cputime, _) ,
                       long <- Long, statistics( cputime, [_,SCpu] ),
                       _Back <- long, statistics( cputime, [_,TCpu] ),
                       T = SCpu-TCpu
                     ),
                   Ts
                ),
         write( ts(Ts) ), nl,
         maplist( arg(1), Ts, Push ),
         write( push(Push) ), nl,
         maplist( arg(2), Ts, Pull ),
         write( pull(Pull) ), nl,
         push <- Push, pull <- Pull,
         <- plot( points, push, ylab='"push + pull (red)"' ),
         r_char( red, Red ),
         <- points( points, pull, col=Red ).

    rtest :-
            y <- rnorm(50),
            <- y,
            x <- rnorm(y),
         <- x11(width=5,height=3.5),
            <- plot(x,y),
            write( 'Press Return to continue...' ), nl,
            read_line_to_codes( user_input, _ ),
            <- dev..off(.),
            Y <- y,
            write( y(Y) ), nl,
            findall( Zx, between(1,9,Zx), Z ),
            z <- Z,
            <- z,
            cars <- [1, 3, 6, 4, 9],
            % cars <- c(1, 3, 6, 4, 9),
            <- pie(cars),
            write( 'Press Return to continue...' ), nl,
            read_line_to_codes( user_input, _ ),
            devoff.

    tut6 :-
            d <- outer(0:9, 0:9),
            fr <- table(outer(d, d, "-")),
            r(plot(as.numeric(names(fr)), fr, type="h",
                xlab="Determinant", ylab="Frequency")).

    tut4b :-
         astate <- [tas,sa,qld,nsw,nsw,nt,wa],
         statef <- factor(state),
         incmeans <- tapply( c(60, 49, 40, 61, 64, 60, 59, 54), statef, mean ),
         <- incmeans.

    logical :-
         t <- [1,2,3,4,5,1],
         s <- t==1,
         <- s,
         S <- s,
         write( s(S) ), nl.

Note, r..eal is sensitive to the amount of stack made available to Yap. With a generous stack, huge objects can be passed between the two systems.

    κρότος;yapr/sureal% yap -s80000 -f -g "ensure_loaded(library(real))"
    YAP 6.3.0 (x86_64-linux): Fri Nov 11 16:06:00 CET 2011
    MYDDAS version MYDDAS-0.9.1
    ?- findall( I, between(1,1000000,I), Is), i <- Is, K <- i, length( K, Len ), write( len(Len) ), nl, fail.
    len(1000000)

## Info

We are grateful to Jan Wielemaker for re-setting and improving the whole of the C code. Many thanks to the developers of YapR for inspiration and some functions.

author
  - Nicos Angelopoulos
  - Vitor Costa Santos
version
  - 0:0:2, 2011/12/07
See also
  - `examples/real_ex.pl`
  - http://www.r-project.org/
license
  - whatever

init_r
  Start an R object. This is done automatically upon loading the library. We should add some checking that none exists here before starting a new one. This is currently not the case.
end_r
  Stop the R object and close the communication channel.
real(Version, Date)
  Version and release date.
<-(+Rvar)
<-(+Rexpr)

  > If Rvar is an atom and a known R object, then print Rvar on R. Else treat the input as an R expression and pass it on R for interpretation. (Throws result away, if expression is not a <- expression itself).

<-(+PLvar, +Rexpr)
<-(+Rexpr1, +Rexpr2)
<-(+Rvar, +PLval)
<-(+PLvar, +Rvar)
  Evaluate Rexpr and store its return value to PLvar.

  Pass Rexpr1 <- Rexpr2 to R.

  Pass Prolog value PLval to Rvar. If PLval is a single value, flat list or list of depth 2 use the low-level interface to pass the value to an R variable (see section on data transfer).

  Place value of Rvar to PLvar.

  Note that all Rexpr* are first processed as described in the section about syntax before passed to R.

r(R)
  Nickname for <-(R).
r(?L, +R)
  Nickname for <-(L,R).
is_rvar(+Rvar)
  True if Rvar is an atom and a known variable in the R environment.
real_type(-Type)
  Enumerate or test all Types known to r..eal. These can be used as i <- Type:Values. Eg.

      i <- int:[1,2.0,3].

r_char(+Atomic, +RcharAtom)
  Wrap an atomic value with double quotes so it can pass as an R char type.
devoff
  Close the current plot devise without any reporting. Short for <- invisible('dev.off'()').