Calling NAG Library Routines from Java - Example 2

NAG Technical Report 2/2009

Calling NAG Library Routines from Java


Start of report   Skip to Example 1   Skip to Example 3   Skip to Example 4   Skip to Example 5   Skip to Example 6

4.2. Example 2

A linear equation solver, function f04arc

Here we show how to call a NAG C Library function which requires input/output array arguments: the function f04arc.

Contents

  1. Function prototype from the NAG C Library Manual
  2. Declaring the native function in our Java program
  3. Compiling the Java program
  4. Generating a header file for use by C
  5. Implementing the native function in C code
  6. Building the shareable library or DLL
  7. Running the program
  8. Quick summary of how to build the linear equation solver example
  1. Function prototype from the NAG C Library Manual

    According to the C Library Manual, the prototype for function f04arc looks like this:

      #include <nag.h>
      #include <nagf04.h>
    
      void f04arc(Integer n, double a[], Integer tda, double b[],
                  double x[], NagError *fail);
    

    The function is designed to solve the set of n linear equations A x = b, where A is an n by n matrix, and b and x are vectors of length n.

    Note that the argument tda is used to notify the NAG Library routine of the trailing dimension of the two-dimensional array A.

  2. Declaring the native function in our Java program

    As with Example 1, we will not attempt to pass the contents of the NagError structure back to Java. In our Java program, we will declare the function like this:

      // Declaration of the Native (C) function
      private native int f04arc(int n, double[] a, int tda, double[] b,
                                 double[] x);
    

    i.e. a method with return type int. Since we are not bothering to use the fail argument, we will use the int return value to send back any error code.

  3. Compiling the Java programHere is the complete source code of our Java program LinearEquations.java.
    public class LinearEquations
    {
    
      // Declaration of the Native (C) function
      private native int f04arc(int n, double[] a, int tda, double[] b,
                                 double[] x);
    
      static
        {
          // The runtime system executes a class's static
          // initializer when it loads the class.
          System.loadLibrary("nagCJavaInterface");
        }
    
      // The main program
      public static void main(String[] args)
        {
          double a[], b[], x[], r[], copyA[];
          int i, j, n, tda, retCode;
    
          // Create an object of class LinearEquations
          LinearEquations lineq = new LinearEquations();
    
          n = 3;
          tda = n;
    
          a = new double[n*n];
          b = new double[n];
          x = new double[n];
          r = new double[n];
          copyA = new double[n*n];
    
          a[0*n + 0] = 33.0;
          a[0*n + 1] = 16.0;
          a[0*n + 2] = 72.0;
          a[1*n + 0] = -24.0;
          a[1*n + 1] = -10.0;
          a[1*n + 2] = -57.0;
          a[2*n + 0] = -8.0;
          a[2*n + 1] = -4.0;
          a[2*n + 2] = -17.0;
    
          b[0] = -359.0;
          b[1] = 281.0;
          b[2] = 85.0;
    
          // Copy matrix A for later use (it gets overwritten by f04arc).
          for (i = 0; i < n * n; i++)
            copyA[i] = a[i];
    
          System.out.println();
          System.out.println("Call of NAG linear equation solver routine f04arc");
          System.out.println();
    
          // Print the input matrix A and vector b
          System.out.println("Input matrix A:");
          for (i = 0; i < n; i++)
            {
              for (j = 0; j < n; j++)
                System.out.print(" " + a[i*n + j]);
              System.out.println();
            }
          System.out.println();
    
          System.out.println("Input vector b:");
          for (i = 0; i < n; i++)
            System.out.println(" " + b[i]);
          System.out.println();
    
          // Call method f04arc of object lineq
          retCode = lineq.f04arc(n, a, tda, b, x);
    
          System.out.print("Return code from f04arc = ");
          System.out.println(retCode);
          System.out.println();
    
          if (retCode == 0)
            {
              // Print the solution vector x
              System.out.print("Solution vector x:\n");
              for (i = 0; i < n; i++)
                System.out.println(" " + x[i]);
              System.out.println();
    
              // Calculate and print residual vector
              for (i = 0; i < n; i++)
                {
                  r[i] = -b[i];
                  for (j = 0; j < n; j++)
                    r[i] += copyA[i*n + j] * x[j];
                }
              System.out.print("Residual vector r = A * x - b:\n");
              for (i = 0; i < n; i++)
                System.out.println(" " + r[i]);
              System.out.println();
            }
        }
    }
    

    Some points to note about this program:

    • Although the the matrix A is two dimensional, we choose to store it in a one-dimensional Java array of type double[]. This makes the Java code slightly harder to read than it would be otherwise, because we need to deal with array subscripting, but it makes the C code that we need to write in the interface library much simpler. On the Java side, we write, for example,
        a[2*n + 2] = -17.0;
      

      instead of

        a[2][2] = -17.0;
      
    • The NAG Library routine f04arc overwrites the array A by its LU factors. We therefore make a copy of A in array copyA, so that we can use it to compute the residual vector r = A x – b as a check of the returned solution.

    We can compile our Java program with the command

      % javac LinearEquations.java
    
  4. Generating a header file for use by CHaving compiled LinearEquations.java, we can use javah to create a C header file:
      % javah -jni LinearEquations
    

    The generated header file, LinearEquations.h, contains this function prototype:

      JNIEXPORT jint JNICALL Java_LinearEquations_f04arc
        (JNIEnv *, jobject, jint, jdoubleArray, jint, jdoubleArray, jdoubleArray);
    

    As before, from the C point of view, our function has an extra two arguments: the Java environment pointer and the Java object. This time, we will need those arguments in our C code.

  5. Implementing the native function in C codeNow that we have created the header file LinearEquations.h, we can write our C code implementation of Java_LinearEquations_f04arc. Here it is, in file LinearEquationsImp.c:
    #include <jni.h>      /* Java Native Interface headers */
    #include "LinearEquations.h"   /* Auto-generated header created by javah -jni */
    
    #include <nag.h>      /* NAG C Library headers */
    #include <nagf04.h>
    
    /* Our C definition of the function f04arc declared in LinearEquations.java */
    JNIEXPORT jint JNICALL Java_LinearEquations_f04arc
      (JNIEnv *env, jobject obj, jint n, jdoubleArray a, jint tda,
       jdoubleArray b, jdoubleArray x)
    {
      static NagError fail;
    
      /* First extract the arrays from Java */
      jdouble *apt, *bpt, *xpt;
      jsize len;
      int i;
    
      apt = (*env)->GetDoubleArrayElements(env, a, 0);
      bpt = (*env)->GetDoubleArrayElements(env, b, 0);
      xpt = (*env)->GetDoubleArrayElements(env, x, 0);
    
      /* Call f04arc */
      fail.print = Nag_FALSE;
      f04arc(n, apt, tda, bpt, xpt, &fail);
    
      /* Release the array elements back to Java */
      (*env)->ReleaseDoubleArrayElements(env, a, apt, 0);
      (*env)->ReleaseDoubleArrayElements(env, b, bpt, 0);
      (*env)->ReleaseDoubleArrayElements(env, x, xpt, 0);
    
      return fail.code;
    }
    

    Points to note:

    • As before, we must include the appropriate NAG C Library header files, and also declare a variable fail of type NagError to pass as the fail argument to f04arc.
    • We cannot access the elements of array arguments a, b and x directly, because they are not C-style arrays but rather Java-style arrays of type jdoubleArray. Trying to access the array elements directly would lead to catastrophe. Instead, we must convert them to C-style double arrays, using the JNI function GetDoubleArrayElements. This function is declared in the JNI header file jni.h like this:
        jdouble * (JNICALL *GetDoubleArrayElements)
          (JNIEnv *env, jdoubleArray array, jboolean *isCopy);
      

      GetDoubleArrayElements is accessed through the JNIEnv pointer, *env. Given the array of type jdoubleArray, it returns a pointer to an array of elements of type jdouble which can safely be manipulated by C. The output argument isCopy tells us whether Java made a copy of the array, or just passed us a pointer to the elements in situ. Here, we are not interested which happened.

      Our C program therefore makes three calls of GetDoubleArrayElements, one for each array argument. The returned pointers are passed directly to the NAG Library function f04arc.

    • After return from the NAG Library, we need to tell Java that we are finished with the array pointers that it gave us. We therefore make three calls of function ReleaseDoubleArrayElements, declared in jni.h as
        void (JNICALL *ReleaseDoubleArrayElements)
          (JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode);
      

      We need to do this for two reasons: to ensure that our results get copied back to the appropriate Java arrays, and so that Java garbage collection can work properly (if we did not do it, Java might leak the memory that it allocated for us).

  6. Building the shareable library or DLLThis step is operating-system dependent.
    • Building on Linux
        % gcc -c -fPIC -I/opt/jdk1.6.0_11/include -I/opt/jdk1.6.0_11/include/linux \
            -I/opt/NAG/cll6a09dhl/include LinearEquationsImp.c
        % ld -G -z defs LinearEquationsImp.o -o libnagCJavaInterface.so \
             /opt/NAG/cll6a09dhl/lib/libnagc_nag.so -lm -lc -lpthread
      

      Recall that on other UNIX machines it may be necessary to add further libraries at link time - see note.

    • Building under Microsoft Windows using Visual C++
        C:\> cl -Ic:\jdk1.6.0_11\include -Ic:\jdk1.6.0_11\include\win32
               -I"c:\NAG\CL09\Program Files\NAG\clw3209dal\include" /Gz -LD LinearEquationsImp.c
               "c:\Program Files\NAG\CL09\clw3209dal\lib\CLW3209DA_nag.lib" -FenagCJavaInterface.dll
      

    The compiler flags used were described in Section 7 of Example 1.

  7. Running the programAssuming that all has gone well, we can run the program using the command
      % java LinearEquations
    

    The expected output looks like this:

        Call of NAG linear equation solver routine f04arc
    
        Input matrix A:
         33.0 16.0 72.0
         -24.0 -10.0 -57.0
         -8.0 -4.0 -17.0
    
        Input vector b:
         -359.0
         281.0
         85.0
    
        Return code from f04arc = 0
    
        Solution vector x:
         1.0
         -2.0
         -5.0
    
        Residual vector r = A * x - b:
         0.0
         0.0
         0.0
    

    (If you get an error message saying that a library cannot be located, see the tip given in Example 1).

  8. Quick summary of how to build the linear equation solver exampleGiven the two source files LinearEquations.java and LinearEquationsImp.c, issue the following commands:
    • Compile the Java class:

        % javac LinearEquations.java
      
    • Create the header file:

        % javah -jni LinearEquations
      
    • Compile the interface library:
      • (Linux)

          % gcc -c -fPIC -I/opt/jdk1.6.0_11/include -I/opt/jdk1.6.0_11/include/linux \
              -I/opt/NAG/cll6a09dhl/include LinearEquationsImp.c
          % ld -G -z defs LinearEquationsImp.o -o libnagCJavaInterface.so \
               /opt/NAG/cll6a09dhl/lib/libnagc_nag.so -lm -lc -lpthread
        

        where /opt/jdk1.6.0_11/include, /opt/jdk1.6.0_11/include/linux, /opt/NAG/cll6a09dhl/include and /opt/NAG/cll6a09dhl/lib are directory names appropriate to your Java and NAG C Library installations.

      • (Microsoft Windows/Visual C++)

          C:\> cl -Ic:\jdk1.6.0_11\include -Ic:\jdk1.6.0_11\include\win32
                 -I"c:\Program Files\NAG\CL09\clw3209dal\include" /Gz -LD LinearEquationsImp.c
                 "c:\Program Files\NAG\CL09\clw3209dal\lib\CLW3209DA_nag.lib" -FenagCJavaInterface.dll
        

        where c:\jdk1.6.0_11\include, c:\jdk1.6.0_11\include\win32, "c:\Program Files\NAG\CL09\clw3209dal\include" and "c:\Program Files\NAG\CL09\clw3209dal\lib" are directory names appropriate to your Java and NAG C Library installations.

    • Run the Java program:

        % java LinearEquations
      

Start of report   Skip to Example 1   Skip to Example 3   Skip to Example 4   Skip to Example 5   Skip to Example 6
Copyright 2009 Numerical Algorithms Group

Page last updated 2011-01-25 annak

[NP3671]