bedatadriven / renjin

JVM-based interpreter for the R language for the statistical analysis.
https://www.renjin.org
GNU General Public License v2.0
515 stars 82 forks source link

cmdscale throwing org.renjin.eval.EvalException when parameter add=TRUE #492

Open ErickPol opened 4 years ago

ErickPol commented 4 years ago

cmdscale is a function included in the stats package, which is included by default in the R environment (same applies for the base Renjin package). I am able to execute the function cmdscale(matrix, k=1) in Renjin with no issues. However, when using the parameter 'add=TRUE' - as in cmdscale(matrix, k=1, add=TRUE) - Renjin throws an org.renjin.eval.EvalException. Both R and Renjin runs are illustrated below:

Renjin run (release version):

public class RenjinInstanceTest {

    private RenjinInstance renjinInstance;

    public RenjinInstanceTest() throws Exception {
        renjinInstance= RenjinInstance.getInstance();
    }

    @Test
    public void cmdScaleTest() throws Exception {
        double[][] testMatrix= new double[4][4];
        testMatrix[0][0] = 1.37680589686909;
        testMatrix[0][1] = 1.36770559031242;
        testMatrix[0][2] = 1.38561245483721;
        testMatrix[0][3] = 1.40217631836104;
        testMatrix[1][0] = 1.36770559031242;
        testMatrix[1][1] = 1.36429765734584;
        testMatrix[1][2] = 1.37562481815626;
        testMatrix[1][3] = 1.38860967955558;
        testMatrix[2][0] = 1.38561245483721;
        testMatrix[2][1] = 1.37562481815626;
        testMatrix[2][2] = 1.37680589686909;
        testMatrix[2][3] = 1.40422475998389;
        testMatrix[3][0] = 1.40217631836104;
        testMatrix[3][1] = 1.38860967955558;
        testMatrix[3][2] = 1.40422475998389;
        testMatrix[3][3] = 1.41266993454097;

        renjinInstance.runCmdScaleFromMatrix(testMatrix);

    }
}

public class RenjinInstance {
    private static RenjinInstance renjinInstance = null;
    ScriptEngine engine;

    // singleton
    public static RenjinInstance getInstance() throws Exception {
        if (renjinInstance == null) {
            renjinInstance = new RenjinInstance ();
        }
        return renjinInstance ;
    }

    private InstanciaRenjin() throws Exception {
        RenjinScriptEngineFactory factory = new RenjinScriptEngineFactory();
        // create a Renjin engine:
        engine = factory.getScriptEngine();
    }

    public double[] runCmdScaleFromMatrix(double[][] testMatrix) throws Exception {

        loadMatrixInREnvironment("testMatrix", testMatrix);

        engine.eval("output <- cmdscale(testMatrix, k=1, add=TRUE)");
        double[] output = ((DoubleVector) 
            engine.eval("output")).toDoubleArray();

        return output;
    }

    public void loadMatrixInREnvironment(String rVariable, double[][] matrix) throws ScriptException {
        engine.put("tempVector", linearizeMatrix(matrix));
        engine.eval(rVariable+ " <- matrix(tempVector, nrow = " + matrix.length + ", ncol = " + 
            matrix[0].length + ")");
    }

    private double[] linearizeMatrix(double[][] matrix) {
        double[] array = new double[matrix.length * matrix[0].length];
        for (int i=0; i< matrix.length; i++) {
            for (int j=0; j < matrix[0].length; j++) {
                array[(i*matrix[0].length) + j] = matrix[i][j];
            }
        }
        return array;
    }
}

This code runs perfectly well if the option add=TRUE is omitted. However, when it is included the following error is shown:

org.renjin.eval.EvalException
at org.renjin.invoke.reflection.FunctionBinding$Overload.invoke(FunctionBinding.java:125)
at org.renjin.invoke.reflection.FunctionBinding.invoke(FunctionBinding.java:172)
at org.renjin.invoke.reflection.FunctionBinding.invoke(FunctionBinding.java:165)
at org.renjin.primitives.Native.delegateToJavaMethod(Native.java:502)
at org.renjin.primitives.Native.redotCall(Native.java:353)
at org.renjin.primitives.R$primitive$$Call.applyPromised(R$primitive$$Call.java:65)
at org.renjin.sexp.BuiltinFunction.apply(BuiltinFunction.java:100)
at org.renjin.sexp.FunctionCall.eval(FunctionCall.java:80)
at org.renjin.primitives.special.IfFunction.apply(IfFunction.java:40)
at org.renjin.sexp.FunctionCall.eval(FunctionCall.java:80)
at org.renjin.primitives.special.AssignLeftFunction.assignLeft(AssignLeftFunction.java:58)
at org.renjin.primitives.special.AssignLeftFunction.apply(AssignLeftFunction.java:42)
at org.renjin.sexp.FunctionCall.eval(FunctionCall.java:80)
at org.renjin.primitives.special.BeginFunction.apply(BeginFunction.java:39)
at org.renjin.sexp.FunctionCall.eval(FunctionCall.java:80)
at org.renjin.primitives.special.IfFunction.apply(IfFunction.java:44)
at org.renjin.sexp.FunctionCall.eval(FunctionCall.java:80)
at org.renjin.primitives.special.BeginFunction.apply(BeginFunction.java:39)
at org.renjin.sexp.FunctionCall.eval(FunctionCall.java:80)
at org.renjin.primitives.special.IfFunction.apply(IfFunction.java:40)
at org.renjin.sexp.FunctionCall.eval(FunctionCall.java:80)
at org.renjin.primitives.special.BeginFunction.apply(BeginFunction.java:39)
at org.renjin.sexp.FunctionCall.eval(FunctionCall.java:80)
at org.renjin.sexp.Closure.applyPromised(Closure.java:200)
at org.renjin.sexp.Closure.apply(Closure.java:133)
at org.renjin.sexp.FunctionCall.eval(FunctionCall.java:80)
at org.renjin.eval.Context.evaluate(Context.java:280)
at org.renjin.primitives.special.DollarFunction.apply(DollarFunction.java:46)
at org.renjin.sexp.FunctionCall.eval(FunctionCall.java:80)
at org.renjin.primitives.special.AssignLeftFunction.assignLeft(AssignLeftFunction.java:58)
at org.renjin.primitives.special.AssignLeftFunction.apply(AssignLeftFunction.java:42)
at org.renjin.sexp.FunctionCall.eval(FunctionCall.java:80)
at org.renjin.primitives.special.BeginFunction.apply(BeginFunction.java:39)
at org.renjin.sexp.FunctionCall.eval(FunctionCall.java:80)
at org.renjin.primitives.special.IfFunction.apply(IfFunction.java:40)
at org.renjin.sexp.FunctionCall.eval(FunctionCall.java:80)
at org.renjin.primitives.special.BeginFunction.apply(BeginFunction.java:39)
at org.renjin.sexp.FunctionCall.eval(FunctionCall.java:80)
at org.renjin.sexp.Closure.applyPromised(Closure.java:200)
at org.renjin.sexp.Closure.apply(Closure.java:133)
at org.renjin.sexp.FunctionCall.eval(FunctionCall.java:80)
at org.renjin.primitives.special.AssignLeftFunction.assignLeft(AssignLeftFunction.java:58)
at org.renjin.primitives.special.AssignLeftFunction.apply(AssignLeftFunction.java:42)
at org.renjin.sexp.FunctionCall.eval(FunctionCall.java:80)
at org.renjin.sexp.ExpressionVector.eval(ExpressionVector.java:85)
at org.renjin.eval.Context.evaluate(Context.java:280)
at org.renjin.script.RenjinScriptEngine.eval(RenjinScriptEngine.java:174)
at org.renjin.script.RenjinScriptEngine.eval(RenjinScriptEngine.java:133)
Caused by: java.lang.NullPointerException
    at java.util.Arrays.copyOfRange(Arrays.java:3737)
    at org.renjin.base.Lapack.rg(Lapack.java:863)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.renjin.invoke.reflection.FunctionBinding$Overload.invoke(FunctionBinding.java:117)

R run :

x <- c(1.37680589686909,1.36770559031242,1.38561245483721,1.40217631836104,1.36770559031242,1.36429765734584,1.37562481815626,1.38860967955558,1.38561245483721,1.37562481815626,1.39062924330846,1.40422475998389,1.40217631836104,1.38860967955558,1.40422475998389,1.41266993454097)
m <- matrix(x, nrow = 4, ncol = 4)
output <- cmdscale(m, k=1, add=TRUE)

The code runs fine and generates the desired output

akbertram commented 4 years ago

It looks like there is an error in our bridge code between Renjin and the LAPACK routine: https://github.com/bedatadriven/renjin/blob/master/core/src/main/java/org/renjin/base/Lapack.java#L829

I believe the working array right is not being allocated. There's a branch that allocates the array if vectors is true, but otherwise it remains null.

Perhaps you can compare to the original C code here: https://github.com/wch/r-source/blob/trunk/src/modules/lapack/Lapack.c#L278

and correct the issue?