JAVAからNAG数値計算ライブラリを利用する : Example 2

テクニカルレポート

4.2. Example 2

連立1次方程式ソルバ、関数 f04arc

入力/出力引数として行列を必要とする NAG C Library 関数 f04arc をコールする方法を示す。

内容

  1. NAG C Library マニュアルからの関数プロトタイプ
  2. Javaプログラム中でのネイティブ関数の宣言
  3. Javaプログラムのコンパイル
  4. C用のヘッダファイルの生成
  5. ネイティブ関数のCによる実装
  6. 共用ライブラリ/DLLの作成
  7. プログラムの実行
  8. クイックサマリ

  1. NAG C Library マニュアルからの関数プロトタイプ
  2. C Library マニュアルによれば関数 f04arc に対するプロトタイプは次のとおりである。

      #include <nag.h>
      #include <nagf04.h>
     
      void f04arc(Integer n, double a[], Integer tda, double b[],
                  double x[], NagError *fail); 
    
    この関数は n 個の連立1次方程式 A x = b の解を求める。ここに An x n の行列であり、bxn 元の列ベクトルである。

    引数 tda は NAG Library ルーチンに対し2次元行列 A の展開次元(trailing dimension)を通知するために用いられる。

  3. Javaプログラム中でのネイティブ関数の宣言
  4. Example 1 の場合と同様、NagError 構造体の内容をJavaには戻さないものとする。従ってJavaプログラム内では関数を次のように宣言すれば良い。

      // Declaration of the Native (C) function
      private native int f04arc(int n, double[] a, int tda, double[] b,
                                 double[] x); 
    
    これは int を応答として返すメソッドを意味する。ここでは fail 引数は用いず、エラーコードはすべて int 戻り値によって送り返すものとする。

  5. Javaプログラムのコンパイル
  6. 次に示すのはJavaプログラム 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 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();
            }
        }
    } 
    
    このプログラムに関する注意事項:

    • 行列 A は2次元であるが、ここではそれを型が double[] の1次元Java配列上にストアしている。配列添え字の操作が必要となるため、Javaコードはそうでない場合に比べやや読みにくくなっているが、インタフェースライブラリ中に記述する必要のあるCコードははるかに簡単なものとなる。Java側では例えば
        a[2][2] = -17.0; 
      
      と書く代りに
        a[2*n + 2] = -17.0;
      と書くことになる。 
      

    • NAG Library ルーチン f04arc は行列 ALU 要素によって上書きする。このため A のコピーを配列 copyA 中に取り、それを用いて残差ベクトル r = A x – b の計算を行う。

    Javaプログラムをコンパイルするには次のコマンドを使用すれば良い。

      % javac LinearEquations.java 
    

  7. C用のヘッダファイルの生成
  8. LinearEquations.javaコンパイルが終わると、javah を使ってCヘッダファイルを作成することができる。

      % javah -jni LinearEquations 
    
    生成されたヘッダファイル LinearEquations.h には次の関数プロトタイプが含まれている。
      JNIEXPORT jint JNICALL Java_LinearEquations_f04arc
        (JNIEnv *, jobject, jint, jdoubleArray, jint, 
         jdoubleArray, jdoubleArray); 
    

    前の場合と同様、Cの視点からすると、我々の関数はJava環境ポインタとJavaオブジェクトという2つの引数を余計に含んでいる。今回の場合にはCコード中でもこれらの引数が必要となる。

  9. ネイティブ関数のCによる実装
  10. ヘッダファイル LinearEquations.h が作成できたところで、今度は Java_LinearEquations_f04arc のCコードによる実装に移る。次はファイル LinearEquationsImp.c の内容を記したものである。

    #include <jni.h>      /* Java Native Interface headers */
    #include "LinearEquations.h"   /* Auto-generated by javah -jni */
     
    #include <nag.h>      /* NAG C Library headers */
    #include <nagf04.h>
     
    /* Our C definition of 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;
    } 
    
    注意事項:

    • 前の場合と同様、適切なNAG C Library ヘッダファイルをインクルードすると共に、f04arc へのfail引数として引渡す型 NagError の変数 fail も宣言しておかなくてはならない。

    • 配列引数の要素 a, b, x を直接アクセスすることはできない。それらはCスタイルの配列ではなく、型が jdoubleArray のJavaスタイル配列だからである。配列要素を直接アクセスしようとすると破滅的な事態が生じるので、JNI 関数 GetDoubleArrayElements を用いてCスタイルの倍精度配列に変換しなくてはならない。この関数は JNI ヘッダファイル jni.h 中で次のように宣言する。
        jdouble * (JNICALL *GetDoubleArrayElements)
          (JNIEnv *env, jdoubleArray array, jboolean *isCopy); 
      
      GetDoubleArrayElements JNIEnv ポインタ *env を介してアクセスされる。型が jdoubleArray の配列が与えられると、それは型が jdouble の配列要素に対するポインタ(Cで安全に操作できる)を応答として返す。出力引数 isCopy はJavaが配列のコピーを作成したか、あるいは単に要素へのポインタを引渡しただけなのかどうかを示す。今回の場合はどちらであっても構わない。

      上記Cプログラム中ではそれぞれの配列引数ごとに GetDoubleArrayElements を計3回コールする。戻り値であるポインタはそのまま NAG Library 関数 f04arc に引渡される。

    • NAG Library から戻った後、Javaに対し配列ポインタを使い終えたことを伝える必要がある。そこで関数 ReleaseDoubleArrayElements を3回コールするわけであるが、それは jni.h 中に次のように宣言されている。
        void (JNICALL *ReleaseDoubleArrayElements)
          (JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode); 
      

      この操作は2つの理由によって必要となる。結果が適切なJava配列に反映されること、それによってJavaのガベージコレクションが適切に行われるようにすることの2点である(これを行わなかった場合にはJavaによるメモリ解放が正しく行われないことが起り得る)。

  11. 共用ライブラリ/DLLの作成
  12. このステップはOS依存となる。

    • Linux上での作成

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

      他のUNIX系マシン上ではさらなるライブラリの追加がリンク時に必要となる場合がある(Example 1 の注意参照)。

    • Windows上でのVisual C++による作成

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

    関連するコンパイラフラグについては Example 1 セクション7 を参照されたい。

  13. プログラムの実行
  14. これまでの操作がすべて正常に行われたとすると、次のコマンドによってプログラムを実行できる。

      % java LinearEquations 
    
    期待される出力は次のとおり。

        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 
    

    (ライブラリが見つけられないという趣旨のエラーメッセージが発行された場合には、Example 1 ヒント を参照)

  15. クイックサマリ
  16. 2つのソースファイル LinearEquations.javaLinearEquationsImp.c が与えられたとして、次のコマンドを発行する。

    • Javaクラスのコンパイル:
        % javac LinearEquations.java 
      
    • ヘッダファイルの作成:
        % javah -jni LinearEquations 
      
    • インタフェースライブラリのコンパイル:

      • (Linux)
          % gcc -c -fPIC -I/opt/jdk1.6.0_11/include \
              -I/opt/jdk1.6.0_11/include/linux \
              -I/opt/NAG/cll6a09dgl/include LinearEquationsImp.c
          % ld -G -z defs LinearEquationsImp.o \
               -o libnagCJavaInterface.so \
               /opt/NAG/cll6a09dgl/lib/libnagc_nag.so -lm -lc -lpthread 
        
        ただしディレクトリ名称 /opt/jdk1.6.0_11/include, /opt/jdk1.6.0_11/include/linux, /opt/NAG/cll6a09dgl/include, /opt/NAG/cll6a09dgl/lib はJavaと NAG C Library のインストレーションに合せて適宜調整を要す。

      • (Windows/Visual C++)
          C:\> cl -Ic:\jdk1.6.0_11\include
                 -Ic:\jdk1.6.0_11\include\win32
                 -I"c:\Program Files\NAG\cldll094zl\include"
                 /Gz -LD LinearEquationsImp.c
                 "c:\Program Files\cldll094zl\lib\CLDLL094Z_nag.lib"
                 -FenagCJavaInterface.dll 
        
        ただしディレクトリ名称 c:\jdk1.6.0_11\include, c:\jdk1.6.0_11\include\win32, "c:\Program Files\NAG\cldll094zl\include", "c:\Program Files\NAG\cldll094zl\lib" はJavaと NAG C Library のインストレーションに合せて適宜調整を要す。

    • Javaプログラムの実行:
        % java LinearEquations
      

Copyright 2009 Numerical Algorithms Group
Page last updated 2009-04-21 14:11:48 mick
[NP3671]


関連情報
Privacy Policy  /  Trademarks