View Javadoc
1   /*
2    * The baseCode project
3    * 
4    * Copyright (c) 2008-2019 University of British Columbia
5    * 
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *       http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   *
18   */
19  package ubic.basecode.util.r;
20  
21  import java.util.ArrayList;
22  import java.util.List;
23  
24  import org.rosuda.REngine.REXP;
25  import org.rosuda.REngine.REXPMismatchException;
26  import org.rosuda.REngine.REngine;
27  import org.rosuda.REngine.REngineException;
28  import org.rosuda.REngine.RList;
29  import org.rosuda.REngine.JRI.JRIEngine;
30  
31  import ubic.basecode.dataStructure.matrix.DoubleMatrix;
32  import ubic.basecode.dataStructure.matrix.DoubleMatrixFactory;
33  
34  /**
35   * R connection implementation that uses the dynamic library interface JRI. For this to work the user must have
36   * libjri.so or jri.dll and libr.so in their java.library.path.
37   * 
38   * @author paul
39   * 
40   * @see RConnectionFactory
41   */
42  public class JRIClient extends AbstractRClient {
43  
44      private static REngine connection = null;
45  
46      static {
47          try {
48              System.loadLibrary( "jri" );
49  
50              /*
51               * The jri engine is static in JRIEngine, so this is okay.
52               */
53              connection = new JRIEngine();
54          } catch ( REngineException e ) {
55              throw new RuntimeException( "JRI could not be initilized" );
56          } catch ( UnsatisfiedLinkError e ) {
57              // oh well...
58              log.warn( e + " " + System.getProperty( "java.library.path" ) );
59          }
60      }
61  
62      public static boolean ready() {
63          return connection != null;
64      }
65  
66      /*
67       * (non-Javadoc)
68       * 
69       * @see ubic.basecode.util.RClient#assign(java.lang.String, double[])
70       */
71      @Override
72      public void assign( String argName, double[] arg ) {
73          try {
74              connection.assign( argName, arg );
75          } catch ( REngineException e ) {
76              throw new RuntimeException( e );
77          }
78      }
79  
80      /*
81       * (non-Javadoc)
82       * 
83       * @see ubic.basecode.util.RClient#assign(java.lang.String, int[])
84       */
85      @Override
86      public void assign( String arg0, int[] arg1 ) {
87          try {
88              connection.assign( arg0, arg1 );
89          } catch ( REngineException e ) {
90              throw new RuntimeException( e );
91          }
92      }
93  
94      /*
95       * (non-Javadoc)
96       * 
97       * @see ubic.basecode.util.RClient#assign(java.lang.String, java.lang.String)
98       */
99      @Override
100     public void assign( String sym, String ct ) {
101         try {
102             connection.assign( sym, ct );
103         } catch ( REngineException e ) {
104             throw new RuntimeException( e );
105         }
106     }
107 
108     /*
109      * (non-Javadoc)
110      * 
111      * @see ubic.basecode.util.RClient#assign(java.lang.String, java.lang.String[])
112      */
113     @Override
114     public void assign( String argName, String[] array ) {
115         try {
116             connection.assign( argName, array );
117         } catch ( REngineException e ) {
118             throw new RuntimeException( e );
119         }
120     }
121 
122     @Override
123     public void disconnect() {
124         // noop
125     }
126 
127     @Override
128     public REXP eval( String command ) {
129         REXP result;
130         int key = connection.lock();
131         try {
132 
133             result = connection.parseAndEval( "try(" + command + ", silent=T)" );
134 
135             if ( result == null ) {
136                 throw new RuntimeException( "Error from R, could not sucessfully evaluate: " + command );
137             }
138 
139             if ( !result.isString() ) {
140                 return result;
141             }
142             String a = result.asString();
143 
144             /*
145              * There is no better way to do this, apparently.
146              */
147             if ( a != null && a.startsWith( "Error" ) ) {
148                 throw new RuntimeException( "Error from R when running " + command + ": " + a );
149             }
150 
151             return result;
152         } catch ( REngineException e ) {
153             throw new RuntimeException( e );
154         } catch ( REXPMismatchException e ) {
155             throw new RuntimeException( e );
156         } finally {
157             connection.unlock( key );
158         }
159     }
160 
161     /*
162      * (non-Javadoc)
163      * 
164      * @see ubic.basecode.util.RClient#getLastError()
165      */
166     @Override
167     public String getLastError() {
168         return "Sorry, no information";
169     }
170 
171     @Override
172     public boolean isConnected() {
173         return true;
174     }
175 
176     /*
177      * (non-Javadoc)
178      * 
179      * @see ubic.basecode.util.RClient#retrieveMatrix(java.lang.String)
180      */
181     @Override
182     public DoubleMatrix<String, String> retrieveMatrix( String variableName ) {
183         log.debug( "Retrieving " + variableName );
184         REXP r = this.eval( variableName );
185         if ( r == null ) throw new IllegalArgumentException( variableName + " not found in R context" );
186 
187         double[][] results;
188         try {
189             results = r.asDoubleMatrix();
190         } catch ( REXPMismatchException e ) {
191             throw new RuntimeException( e );
192         }
193 
194         if ( results == null ) throw new RuntimeException( "Failed to get back matrix for variable " + variableName );
195 
196         DoubleMatrix<String, String> resultObject = DoubleMatrixFactory.dense( results );
197 
198         retrieveRowAndColumnNames( variableName, resultObject );
199         return resultObject;
200 
201     }
202 
203     /*
204      * (non-Javadoc)
205      * 
206      * @see ubic.basecode.util.RClient#voidEval(java.lang.String)
207      */
208     @Override
209     public void voidEval( String command ) {
210         eval( command );
211     }
212 
213     /**
214      * Get the dimnames associated with the matrix variable row and column names, if any, and assign them to the
215      * resultObject NamedMatrix
216      * 
217      * @param variableName a matrix in R
218      * @param resultObject corresponding NamedMatrix we are filling in.
219      */
220     private void retrieveRowAndColumnNames( String variableName, DoubleMatrix<String, String> resultObject ) {
221         log.debug( "Getting row & column names names" );
222 
223         REXP r1 = this.eval( "dimnames(" + variableName + ")" );
224         RList asList;
225         try {
226             asList = r1.asList();
227         } catch ( REXPMismatchException e1 ) {
228             log.warn( "Matrix had no valid dimnames" );
229             return;
230         }
231 
232         if ( asList == null ) {
233             return;
234         }
235 
236         REXP rowNamesREXP = asList.at( 0 );
237         REXP colNamesREXP = asList.at( 1 );
238 
239         if ( rowNamesREXP != null ) {
240             log.debug( "Got row names" );
241             String[] rowNamesAr;
242             try {
243                 rowNamesAr = rowNamesREXP.asStrings();
244             } catch ( REXPMismatchException e ) {
245                 log.warn( "Invalid rownames" );
246                 return;
247             }
248             List<String> rowNames = new ArrayList<String>();
249             for ( String rowName : rowNamesAr ) {
250                 rowNames.add( rowName );
251             }
252             resultObject.setRowNames( rowNames );
253         } else {
254             log.debug( "No row names" );
255         }
256 
257         // Getting the column names.
258         if ( colNamesREXP != null ) {
259             String[] colNamesAr;
260             try {
261                 colNamesAr = colNamesREXP.asStrings();
262             } catch ( REXPMismatchException e ) {
263                 return;
264             }
265             log.debug( "Got column names" );
266             List<String> colNames = new ArrayList<String>();
267             for ( String colName : colNamesAr ) {
268                 colNames.add( colName );
269             }
270             resultObject.setColumnNames( colNames );
271         } else {
272             log.debug( "No column names" );
273         }
274         if ( log.isDebugEnabled() ) log.debug( resultObject.toString() );
275     }
276 
277 }